summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp49
-rw-r--r--Android.bp7
-rw-r--r--OWNERS3
-rw-r--r--PREUPLOAD.cfg2
-rw-r--r--PREUPLOAD_OWNERS2
-rw-r--r--apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java67
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java4
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java14
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java21
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java12
-rw-r--r--api/Android.bp15
-rw-r--r--api/ApiDocs.bp32
-rw-r--r--api/api.go3
-rw-r--r--boot/Android.bp10
-rw-r--r--boot/preloaded-classes1
-rw-r--r--cmds/screencap/screencap.cpp1
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/api/current.txt209
-rw-r--r--core/api/lint-baseline.txt10
-rw-r--r--core/api/module-lib-current.txt13
-rw-r--r--core/api/system-current.txt328
-rw-r--r--core/api/system-lint-baseline.txt2
-rw-r--r--core/api/test-current.txt21
-rw-r--r--core/api/test-lint-baseline.txt6
-rw-r--r--core/java/android/app/Activity.java68
-rw-r--r--core/java/android/app/ActivityManager.java12
-rw-r--r--core/java/android/app/ActivityManagerInternal.java10
-rw-r--r--core/java/android/app/ActivityThread.java88
-rw-r--r--core/java/android/app/AppOpsManager.aidl1
-rw-r--r--core/java/android/app/AppOpsManager.java225
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java8
-rw-r--r--core/java/android/app/BroadcastStickyCache.java101
-rw-r--r--core/java/android/app/CameraCompatTaskInfo.java26
-rw-r--r--core/java/android/app/ContextImpl.java21
-rw-r--r--core/java/android/app/IApplicationThread.aidl4
-rw-r--r--core/java/android/app/Notification.java78
-rw-r--r--core/java/android/app/NotificationManager.java224
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java61
-rw-r--r--core/java/android/app/ResourcesManager.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java32
-rw-r--r--core/java/android/app/TEST_MAPPING8
-rw-r--r--core/java/android/app/TaskInfo.java12
-rw-r--r--core/java/android/app/WallpaperManager.java1
-rw-r--r--core/java/android/app/activity_manager.aconfig17
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java7
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java93
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig10
-rw-r--r--core/java/android/app/appfunctions/AppFunctionException.java14
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java4
-rw-r--r--core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java17
-rw-r--r--core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java45
-rw-r--r--core/java/android/app/compat/ChangeIdStateCache.java16
-rw-r--r--core/java/android/app/contextualsearch/flags.aconfig4
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java3
-rw-r--r--core/java/android/app/jank/JankTracker.java16
-rw-r--r--core/java/android/app/notification.aconfig14
-rw-r--r--core/java/android/app/ondeviceintelligence/OWNERS6
-rw-r--r--core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig17
-rw-r--r--core/java/android/app/performance.aconfig8
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java2
-rw-r--r--core/java/android/app/servertransaction/ConfigurationChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java4
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java2
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java23
-rw-r--r--core/java/android/app/supervision/SupervisionManagerInternal.java31
-rw-r--r--core/java/android/app/supervision/flags.aconfig8
-rw-r--r--core/java/android/app/wallpaper/WallpaperDescription.java8
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java115
-rw-r--r--core/java/android/companion/AssociationRequest.java21
-rw-r--r--core/java/android/companion/DeviceId.java11
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java6
-rw-r--r--core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl5
-rw-r--r--core/java/android/content/Context.java59
-rw-r--r--core/java/android/content/ContextWrapper.java6
-rw-r--r--core/java/android/content/EventLogTags.logtags2
-rw-r--r--core/java/android/content/Intent.java30
-rw-r--r--core/java/android/content/pm/PackageManager.java84
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/content/pm/SigningInfo.java16
-rw-r--r--core/java/android/content/pm/SigningInfoException.java4
-rw-r--r--core/java/android/content/pm/TEST_MAPPING11
-rw-r--r--core/java/android/content/pm/flags.aconfig6
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java3
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java166
-rw-r--r--core/java/android/content/res/flags.aconfig8
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java5
-rw-r--r--core/java/android/credentials/flags.aconfig30
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java88
-rw-r--r--core/java/android/database/sqlite/SQLiteRawStatement.java18
-rw-r--r--core/java/android/hardware/contexthub/HubEndpoint.java40
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointSession.java16
-rw-r--r--core/java/android/hardware/contexthub/HubServiceInfo.java15
-rw-r--r--core/java/android/hardware/contexthub/IContextHubEndpoint.aidl9
-rw-r--r--core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl4
-rw-r--r--core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java6
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java26
-rw-r--r--core/java/android/hardware/display/DisplayTopologyGraph.java43
-rw-r--r--core/java/android/hardware/input/InputSettings.java16
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java8
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig23
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java73
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl2
-rw-r--r--core/java/android/hardware/radio/ProgramSelector.java30
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java7
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java9
-rw-r--r--core/java/android/net/EventLogTags.logtags2
-rw-r--r--core/java/android/net/flags.aconfig8
-rw-r--r--core/java/android/net/http/X509TrustManagerExtensions.java4
-rw-r--r--core/java/android/net/metrics/DnsEvent.java6
-rw-r--r--core/java/android/os/Build.java19
-rw-r--r--core/java/android/os/Bundle.java24
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java234
-rw-r--r--core/java/android/os/ConcurrentMessageQueue/MessageQueue.java168
-rw-r--r--core/java/android/os/Debug.java42
-rw-r--r--core/java/android/os/EventLogTags.logtags2
-rw-r--r--core/java/android/os/Handler.java4
-rw-r--r--core/java/android/os/IHintManager.aidl34
-rw-r--r--core/java/android/os/IpcDataCache.java66
-rw-r--r--core/java/android/os/LegacyMessageQueue/MessageQueue.java137
-rw-r--r--core/java/android/os/Looper.java43
-rw-r--r--core/java/android/os/OWNERS10
-rw-r--r--core/java/android/os/Parcel.java264
-rw-r--r--core/java/android/os/Process.java16
-rw-r--r--core/java/android/os/ServiceManager.java6
-rw-r--r--core/java/android/os/ServiceManagerNative.java5
-rw-r--r--core/java/android/os/TestLooperManager.java51
-rw-r--r--core/java/android/os/UserManager.java28
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl7
-rw-r--r--core/java/android/os/instrumentation/IOffsetCallback.aidl28
-rw-r--r--core/java/android/os/instrumentation/MethodDescriptorParser.java82
-rw-r--r--core/java/android/permission/IPermissionManager.aidl2
-rw-r--r--core/java/android/permission/PermissionManager.java109
-rw-r--r--core/java/android/permission/flags.aconfig26
-rw-r--r--core/java/android/preference/PreferenceActivity.java25
-rw-r--r--core/java/android/provider/Settings.java45
-rw-r--r--core/java/android/provider/flags.aconfig8
-rw-r--r--core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java19
-rw-r--r--core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java19
-rw-r--r--core/java/android/security/flags.aconfig8
-rw-r--r--core/java/android/security/net/config/CertificatesEntryRef.java10
-rw-r--r--core/java/android/security/net/config/KeyStoreConfigSource.java4
-rw-r--r--core/java/android/security/net/config/NetworkSecurityConfig.java22
-rw-r--r--core/java/android/security/net/config/XmlConfigSource.java5
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig8
-rw-r--r--core/java/android/service/autofill/FillEventHistory.java2
-rw-r--r--core/java/android/service/notification/RateEstimator.java (renamed from services/core/java/com/android/server/notification/RateEstimator.java)8
-rw-r--r--core/java/android/service/notification/ZenDeviceEffects.java169
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java47
-rw-r--r--core/java/android/service/notification/ZenModeDiff.java6
-rw-r--r--core/java/android/service/ondeviceintelligence/OWNERS1
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java39
-rw-r--r--core/java/android/speech/tts/EventLogTags.logtags2
-rw-r--r--core/java/android/text/TextUtils.java2
-rw-r--r--core/java/android/view/Choreographer.java143
-rw-r--r--core/java/android/view/Display.java54
-rw-r--r--core/java/android/view/DisplayInfo.java17
-rw-r--r--core/java/android/view/EventLogTags.logtags4
-rw-r--r--core/java/android/view/IWindowManager.aidl23
-rw-r--r--core/java/android/view/InsetsSource.java25
-rw-r--r--core/java/android/view/InsetsState.java4
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/ViewStructure.java12
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java14
-rw-r--r--core/java/android/view/autofill/AutofillManager.java60
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl2
-rw-r--r--core/java/android/view/flags/scroll_capture.aconfig9
-rw-r--r--core/java/android/view/flags/scroll_feedback_flags.aconfig6
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java7
-rw-r--r--core/java/android/view/textclassifier/TextClassificationManager.java30
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java52
-rw-r--r--core/java/android/webkit/EventLogTags.logtags2
-rw-r--r--core/java/android/widget/AbsListView.java6
-rw-r--r--core/java/android/widget/RemoteViews.java73
-rw-r--r--core/java/android/widget/ScrollView.java4
-rw-r--r--core/java/android/widget/TextView.java21
-rw-r--r--core/java/android/window/BackProgressAnimator.java3
-rw-r--r--core/java/android/window/ScreenCapture.java39
-rw-r--r--core/java/android/window/TaskSnapshot.java14
-rw-r--r--core/java/android/window/TransitionRequestInfo.java199
-rw-r--r--core/java/android/window/WindowContainerTransaction.java115
-rw-r--r--core/java/android/window/WindowContext.java23
-rw-r--r--core/java/android/window/WindowContextController.java15
-rw-r--r--core/java/android/window/WindowTokenClient.java2
-rw-r--r--core/java/android/window/WindowTokenClientController.java15
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig50
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig41
-rw-r--r--core/java/com/android/internal/app/AlertController.java4
-rw-r--r--core/java/com/android/internal/app/EventLogTags.logtags2
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl1
-rw-r--r--core/java/com/android/internal/app/NfcResolverActivity.java4
-rw-r--r--core/java/com/android/internal/content/om/OverlayManagerImpl.java14
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java6
-rw-r--r--core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java28
-rw-r--r--core/java/com/android/internal/jank/EventLogTags.logtags2
-rw-r--r--core/java/com/android/internal/logging/EventLogTags.logtags2
-rw-r--r--core/java/com/android/internal/os/TEST_MAPPING2
-rw-r--r--core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java2
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java4
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl7
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java68
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java8
-rw-r--r--core/java/com/android/internal/view/ScrollCaptureInternal.java32
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java3
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java69
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java2
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java21
-rw-r--r--core/java/com/android/internal/widget/flags.aconfig9
-rw-r--r--core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java110
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java69
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java15
-rw-r--r--core/java/com/android/server/pm/pkg/AndroidPackage.java2
-rw-r--r--core/java/org/chromium/arc/EventLogTags.logtags2
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.cpp73
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.h184
-rw-r--r--core/jni/android_database_SQLiteRawStatement.cpp54
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp51
-rw-r--r--core/jni/android_hardware_display_DisplayTopology.cpp155
-rw-r--r--core/jni/android_hardware_display_DisplayTopology.h32
-rw-r--r--core/jni/android_media_AudioFormat.h60
-rw-r--r--core/jni/android_os_SELinux.cpp18
-rw-r--r--core/jni/android_util_Process.cpp27
-rw-r--r--core/jni/android_view_SurfaceControlActivePictureListener.cpp5
-rw-r--r--core/jni/android_window_ScreenCapture.cpp11
-rw-r--r--core/jni/com_android_internal_content_FileSystemUtils.cpp16
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp7
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp2
-rw-r--r--core/jni/com_android_internal_util_ArrayUtils.cpp119
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-rw-r--r--core/res/Android.bp1
-rw-r--r--core/res/AndroidManifest.xml78
-rw-r--r--core/res/res/color/btn_material_filled_background_color_watch.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml)6
-rw-r--r--core/res/res/color/btn_material_filled_content_color_watch.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_background_color.xml)6
-rw-r--r--core/res/res/color/btn_material_filled_tonal_background_color_watch.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_content_color.xml)6
-rw-r--r--core/res/res/color/btn_material_filled_tonal_content_color_watch.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml)6
-rw-r--r--core/res/res/color/btn_material_outlined_background_color_watch.xml (renamed from core/res/res/color-watch-v36/btn_material_outlined_background_color.xml)6
-rw-r--r--core/res/res/color/input_method_switch_on_item.xml4
-rw-r--r--core/res/res/color/notification_expand_button_state_tint.xml4
-rw-r--r--core/res/res/color/system_on_surface_disabled.xml2
-rw-r--r--core/res/res/color/system_outline_disabled.xml2
-rw-r--r--core/res/res/color/system_surface_disabled.xml2
-rw-r--r--core/res/res/drawable/btn_background_material_filled_tonal_watch.xml (renamed from core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml)2
-rw-r--r--core/res/res/drawable/btn_background_material_filled_watch.xml (renamed from core/res/res/drawable-watch-v36/btn_background_material_filled.xml)2
-rw-r--r--core/res/res/drawable/btn_background_material_outlined_watch.xml (renamed from core/res/res/drawable-watch-v36/btn_background_material_outlined.xml)2
-rw-r--r--core/res/res/drawable/btn_background_material_text_watch.xml (renamed from core/res/res/drawable-watch-v36/btn_background_material_text.xml)0
-rw-r--r--core/res/res/drawable/dialog_alert_button_background_negative_watch.xml (renamed from core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml)2
-rw-r--r--core/res/res/drawable/dialog_alert_button_background_positive_watch.xml (renamed from core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml)2
-rw-r--r--core/res/res/drawable/dialog_alert_button_negative_watch.xml (renamed from core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml)4
-rw-r--r--core/res/res/drawable/dialog_alert_button_positive_watch.xml (renamed from core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml)4
-rw-r--r--core/res/res/drawable/floating_popup_background.xml2
-rw-r--r--core/res/res/drawable/ic_check_watch.xml (renamed from core/res/res/drawable-watch-v36/ic_check.xml)4
-rw-r--r--core/res/res/drawable/ic_close_watch.xml (renamed from core/res/res/drawable-watch-v36/ic_close.xml)4
-rw-r--r--core/res/res/drawable/immersive_cling_bg.xml2
-rw-r--r--core/res/res/drawable/input_method_switch_button.xml2
-rw-r--r--core/res/res/drawable/input_method_switch_item_background.xml2
-rw-r--r--core/res/res/drawable/progress_ring_watch.xml (renamed from core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml)4
-rw-r--r--core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml113
-rw-r--r--core/res/res/layout/alert_dialog_icon_button_watch.xml (renamed from core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml)2
-rw-r--r--core/res/res/layout/alert_dialog_title_watch.xml46
-rw-r--r--core/res/res/layout/alert_dialog_watch.xml138
-rw-r--r--core/res/res/layout/floating_popup_menu_button.xml2
-rw-r--r--core/res/res/layout/floating_popup_overflow_button.xml2
-rw-r--r--core/res/res/layout/immersive_mode_cling.xml6
-rw-r--r--core/res/res/layout/input_method_switch_item_divider.xml2
-rw-r--r--core/res/res/layout/input_method_switch_item_header.xml2
-rw-r--r--core/res/res/layout/input_method_switch_item_new.xml2
-rw-r--r--core/res/res/layout/notification_2025_conversation_face_pile_layout.xml12
-rw-r--r--core/res/res/layout/notification_2025_conversation_header.xml171
-rw-r--r--core/res/res/layout/notification_2025_conversation_icon_container.xml37
-rw-r--r--core/res/res/layout/notification_2025_messaging_group.xml12
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml8
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_call.xml4
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml8
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_messaging.xml8
-rw-r--r--core/res/res/layout/notification_2025_template_conversation.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_call.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_messaging.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_header.xml7
-rw-r--r--core/res/res/layout/notification_2025_text.xml1
-rw-r--r--core/res/res/values-af/strings.xml433
-rw-r--r--core/res/res/values-am/strings.xml25
-rw-r--r--core/res/res/values-ar/strings.xml16
-rw-r--r--core/res/res/values-as/strings.xml25
-rw-r--r--core/res/res/values-az/strings.xml25
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml25
-rw-r--r--core/res/res/values-be/strings.xml25
-rw-r--r--core/res/res/values-bg/strings.xml25
-rw-r--r--core/res/res/values-bn/strings.xml25
-rw-r--r--core/res/res/values-bs/strings.xml25
-rw-r--r--core/res/res/values-ca/strings.xml25
-rw-r--r--core/res/res/values-cs/strings.xml25
-rw-r--r--core/res/res/values-da/strings.xml25
-rw-r--r--core/res/res/values-de/strings.xml25
-rw-r--r--core/res/res/values-el/strings.xml25
-rw-r--r--core/res/res/values-en-rAU/strings.xml25
-rw-r--r--core/res/res/values-en-rCA/strings.xml16
-rw-r--r--core/res/res/values-en-rGB/strings.xml25
-rw-r--r--core/res/res/values-en-rIN/strings.xml25
-rw-r--r--core/res/res/values-es-rUS/strings.xml25
-rw-r--r--core/res/res/values-es/strings.xml25
-rw-r--r--core/res/res/values-et/strings.xml25
-rw-r--r--core/res/res/values-eu/strings.xml25
-rw-r--r--core/res/res/values-fa/strings.xml25
-rw-r--r--core/res/res/values-fi/strings.xml25
-rw-r--r--core/res/res/values-fr-rCA/strings.xml25
-rw-r--r--core/res/res/values-fr/strings.xml25
-rw-r--r--core/res/res/values-gl/strings.xml25
-rw-r--r--core/res/res/values-gu/strings.xml25
-rw-r--r--core/res/res/values-hi/strings.xml25
-rw-r--r--core/res/res/values-hr/strings.xml25
-rw-r--r--core/res/res/values-hu/strings.xml25
-rw-r--r--core/res/res/values-hy/strings.xml25
-rw-r--r--core/res/res/values-in/strings.xml25
-rw-r--r--core/res/res/values-is/strings.xml16
-rw-r--r--core/res/res/values-it/strings.xml16
-rw-r--r--core/res/res/values-iw/strings.xml25
-rw-r--r--core/res/res/values-ja/strings.xml16
-rw-r--r--core/res/res/values-ka/strings.xml16
-rw-r--r--core/res/res/values-kk/strings.xml25
-rw-r--r--core/res/res/values-km/strings.xml27
-rw-r--r--core/res/res/values-kn/strings.xml38
-rw-r--r--core/res/res/values-ko/strings.xml25
-rw-r--r--core/res/res/values-ky/strings.xml16
-rw-r--r--core/res/res/values-lo/strings.xml16
-rw-r--r--core/res/res/values-lt/strings.xml16
-rw-r--r--core/res/res/values-lv/strings.xml25
-rw-r--r--core/res/res/values-mk/strings.xml25
-rw-r--r--core/res/res/values-ml/strings.xml25
-rw-r--r--core/res/res/values-mn/strings.xml25
-rw-r--r--core/res/res/values-mr/strings.xml16
-rw-r--r--core/res/res/values-ms/strings.xml25
-rw-r--r--core/res/res/values-my/strings.xml25
-rw-r--r--core/res/res/values-nb/strings.xml25
-rw-r--r--core/res/res/values-ne/strings.xml25
-rw-r--r--core/res/res/values-nl/strings.xml25
-rw-r--r--core/res/res/values-or/strings.xml27
-rw-r--r--core/res/res/values-pa/strings.xml25
-rw-r--r--core/res/res/values-pl/strings.xml25
-rw-r--r--core/res/res/values-pt-rBR/strings.xml25
-rw-r--r--core/res/res/values-pt-rPT/strings.xml16
-rw-r--r--core/res/res/values-pt/strings.xml25
-rw-r--r--core/res/res/values-ro/strings.xml25
-rw-r--r--core/res/res/values-ru/strings.xml25
-rw-r--r--core/res/res/values-si/strings.xml25
-rw-r--r--core/res/res/values-sk/strings.xml25
-rw-r--r--core/res/res/values-sl/strings.xml16
-rw-r--r--core/res/res/values-sq/strings.xml27
-rw-r--r--core/res/res/values-sr/strings.xml25
-rw-r--r--core/res/res/values-sv/strings.xml25
-rw-r--r--core/res/res/values-sw/strings.xml25
-rw-r--r--core/res/res/values-sw600dp/config.xml2
-rw-r--r--core/res/res/values-ta/strings.xml25
-rw-r--r--core/res/res/values-te/strings.xml27
-rw-r--r--core/res/res/values-th/strings.xml25
-rw-r--r--core/res/res/values-tl/strings.xml25
-rw-r--r--core/res/res/values-tr/strings.xml25
-rw-r--r--core/res/res/values-uk/strings.xml25
-rw-r--r--core/res/res/values-ur/strings.xml25
-rw-r--r--core/res/res/values-uz/strings.xml25
-rw-r--r--core/res/res/values-vi/strings.xml25
-rw-r--r--core/res/res/values-w192dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w195dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w198dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w204dp-round-watch/dimens_material.xml4
-rw-r--r--core/res/res/values-w205dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w208dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w210dp-round-watch/dimens_material.xml8
-rw-r--r--core/res/res/values-w211dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w213dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w216dp/dimens_material.xml (renamed from services/tests/security/intrusiondetection/res/xml/device_admin.xml)12
-rw-r--r--core/res/res/values-w225dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w227dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w228dp/dimens_material.xml4
-rw-r--r--core/res/res/values-w240dp/dimens_material.xml4
-rw-r--r--core/res/res/values-watch-v36/colors.xml18
-rw-r--r--core/res/res/values-watch-v36/dimens_material.xml39
-rw-r--r--core/res/res/values-watch-v36/styles_material.xml105
-rw-r--r--core/res/res/values-watch/config_material.xml10
-rw-r--r--core/res/res/values-watch/styles_device_default.xml40
-rw-r--r--core/res/res/values-watch/styles_device_defaults.xml88
-rw-r--r--core/res/res/values-watch/themes_device_defaults.xml50
-rw-r--r--core/res/res/values-zh-rCN/strings.xml25
-rw-r--r--core/res/res/values-zh-rHK/strings.xml25
-rw-r--r--core/res/res/values-zh-rTW/strings.xml25
-rw-r--r--core/res/res/values-zu/strings.xml25
-rw-r--r--core/res/res/values/attrs.xml176
-rw-r--r--core/res/res/values/attrs_manifest.xml15
-rw-r--r--core/res/res/values/config.xml21
-rw-r--r--core/res/res/values/config_material.xml10
-rw-r--r--core/res/res/values/config_telephony.xml16
-rw-r--r--core/res/res/values/config_watch.xml (renamed from core/res/res/values-watch-v36/config.xml)2
-rw-r--r--core/res/res/values/dimens.xml14
-rw-r--r--core/res/res/values/dimens_watch.xml64
-rw-r--r--core/res/res/values/public-staging.xml31
-rw-r--r--core/res/res/values/styles_device_defaults.xml6
-rw-r--r--core/res/res/values/styles_watch.xml73
-rw-r--r--core/res/res/values/symbols.xml150
-rw-r--r--core/res/res/values/themes_device_defaults.xml4418
-rw-r--r--core/res/res/xml/bookmarks.xml20
-rw-r--r--core/tests/coretests/Android.bp15
-rw-r--r--core/tests/coretests/src/android/app/NotificationManagerTest.java151
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java17
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java17
-rw-r--r--core/tests/coretests/src/android/app/QueuedWorkTest.java22
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java61
-rw-r--r--core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java2
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java50
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt171
-rw-r--r--core/tests/coretests/src/android/os/BinderThreadPriorityTest.java4
-rw-r--r--core/tests/coretests/src/android/os/BuildTest.java14
-rw-r--r--core/tests/coretests/src/android/os/IpcDataCacheTest.java80
-rw-r--r--core/tests/coretests/src/android/os/TestLooperManagerTest.java91
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java96
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java31
-rw-r--r--core/tests/coretests/src/android/window/WindowContextTest.java37
-rw-r--r--core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java243
-rw-r--r--core/tests/overlaytests/device_self_targeting/Android.bp1
-rw-r--r--core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java21
-rw-r--r--core/tests/overlaytests/host/Android.bp18
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java54
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/privapp-permissions-platform.xml7
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java18
-rw-r--r--graphics/java/android/graphics/Paint.java18
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java26
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig7
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt49
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt37
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt66
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt64
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt33
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt54
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt31
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt41
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt228
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt79
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt123
-rw-r--r--libs/WindowManager/Shell/res/color/bubble_drop_target_background_color.xml2
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml2
-rw-r--r--libs/WindowManager/Shell/res/color/open_by_default_settings_dialog_radio_button_color.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_drop_target_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_or_maximize_exit_button_dark.xml (renamed from libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_exit_button_dark.xml)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml7
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_flyout.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_button.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml34
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml24
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/maximize_menu_button.xml19
-rw-r--r--libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml12
-rw-r--r--libs/WindowManager/Shell/shared/Android.bp1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java64
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java330
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java183
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java (renamed from packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java)12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt234
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt212
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java9
-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/pip/phone/PipController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java126
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java340
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt148
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java216
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt199
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt88
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt102
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt149
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt120
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt97
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt173
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java39
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java67
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt92
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt103
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt139
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt68
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxControllerTest.kt39
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxControllerTest.kt39
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt78
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt192
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt359
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt357
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt1409
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt136
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt286
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt8899
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt358
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt97
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt106
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt843
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt164
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt402
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt232
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt151
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java294
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt59
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt74
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java163
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt201
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt224
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt81
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt160
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt178
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt144
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt9
-rw-r--r--libs/androidfw/CursorWindow.cpp16
-rw-r--r--libs/appfunctions/api/current.txt1
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java10
-rw-r--r--libs/hwui/Android.bp5
-rw-r--r--libs/hwui/hwui/Bitmap.cpp26
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp12
-rw-r--r--location/api/system-current.txt814
-rw-r--r--location/java/android/location/BeidouAssistance.java283
-rw-r--r--location/java/android/location/BeidouSatelliteEphemeris.java647
-rw-r--r--location/java/android/location/GalileoAssistance.java283
-rw-r--r--location/java/android/location/GalileoIonosphericModel.java148
-rw-r--r--location/java/android/location/GalileoSatelliteEphemeris.java660
-rw-r--r--location/java/android/location/GlonassAlmanac.java418
-rw-r--r--location/java/android/location/GlonassAssistance.java213
-rw-r--r--location/java/android/location/GlonassSatelliteEphemeris.java623
-rw-r--r--location/java/android/location/GnssAlmanac.java619
-rw-r--r--location/java/android/location/GnssAssistance.aidl18
-rw-r--r--location/java/android/location/GnssAssistance.java310
-rw-r--r--location/java/android/location/GnssCorrectionComponent.java300
-rw-r--r--location/java/android/location/GpsAssistance.java289
-rw-r--r--location/java/android/location/GpsSatelliteEphemeris.java632
-rw-r--r--location/java/android/location/IonosphericCorrection.java112
-rw-r--r--location/java/android/location/KeplerianOrbitModel.java518
-rw-r--r--location/java/android/location/KlobucharIonosphericModel.java252
-rw-r--r--location/java/android/location/LeapSecondsModel.java171
-rw-r--r--location/java/android/location/QzssAssistance.java282
-rw-r--r--location/java/android/location/QzssSatelliteEphemeris.java229
-rw-r--r--location/java/android/location/RealTimeIntegrityModel.java284
-rw-r--r--location/java/android/location/SatelliteEphemerisTime.java151
-rw-r--r--location/java/android/location/TimeModel.java208
-rw-r--r--location/java/android/location/UtcModel.java176
-rw-r--r--location/java/android/location/flags/location.aconfig7
-rw-r--r--media/java/android/media/AudioDeviceInfo.java40
-rw-r--r--media/java/android/media/AudioDevicePort.java21
-rw-r--r--media/java/android/media/AudioFormat.java210
-rw-r--r--media/java/android/media/AudioManager.java23
-rw-r--r--media/java/android/media/AudioPortEventHandler.java20
-rw-r--r--media/java/android/media/AudioSystem.java24
-rw-r--r--media/java/android/media/IMediaRoute2ProviderService.aidl17
-rw-r--r--media/java/android/media/MediaFormat.java18
-rw-r--r--media/java/android/media/MediaRoute2Info.java141
-rw-r--r--media/java/android/media/MediaRoute2ProviderInfo.java19
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java258
-rw-r--r--media/java/android/media/MediaRouter2.java19
-rw-r--r--media/java/android/media/MediaRouter2Manager.java19
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig7
-rw-r--r--media/java/android/media/quality/ActiveProcessingPicture.aidl19
-rw-r--r--media/java/android/media/quality/ActiveProcessingPicture.java86
-rw-r--r--media/java/android/media/quality/IMediaQualityManager.aidl71
-rw-r--r--media/java/android/media/quality/IPictureProfileCallback.aidl2
-rw-r--r--media/java/android/media/quality/ISoundProfileCallback.aidl2
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java128
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java256
-rw-r--r--media/java/android/media/quality/PictureProfile.java31
-rw-r--r--media/java/android/media/quality/PictureProfileHandle.java14
-rw-r--r--media/java/android/media/quality/SoundProfile.java31
-rw-r--r--media/java/android/media/quality/SoundProfileHandle.aidl19
-rw-r--r--media/java/android/media/quality/SoundProfileHandle.java72
-rw-r--r--media/jni/android_media_MediaCodec.cpp12
-rw-r--r--native/android/Android.bp1
-rw-r--r--native/android/dynamic_instrumentation_manager.cpp42
-rw-r--r--native/android/include_platform/android/dynamic_instrumentation_manager.h46
-rw-r--r--native/android/libandroid.map.txt18
-rw-r--r--native/android/performance_hint.cpp140
-rw-r--r--native/android/system_health.cpp278
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp39
-rw-r--r--nfc/Android.bp4
-rw-r--r--nfc/OWNERS2
-rw-r--r--nfc/api/system-current.txt18
-rw-r--r--nfc/java/android/nfc/NfcOemExtension.java15
-rw-r--r--nfc/java/android/nfc/T4tNdefNfcee.java24
-rw-r--r--nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java127
-rw-r--r--nfc/java/android/nfc/cardemulation/ApduServiceInfo.java14
-rw-r--r--nfc/java/android/nfc/cardemulation/CardEmulation.java21
-rw-r--r--nfc/java/android/nfc/cardemulation/HostApduService.java2
-rw-r--r--nfc/java/android/nfc/cardemulation/OWNERS2
-rw-r--r--nfc/java/android/nfc/dta/OWNERS2
-rw-r--r--nfc/java/android/nfc/tech/OWNERS2
-rw-r--r--nfc/lint-baseline.xml211
-rw-r--r--nfc/tests/Android.bp27
-rw-r--r--nfc/tests/AndroidManifest.xml2
-rw-r--r--nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java95
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java6
-rw-r--r--packages/CrashRecovery/framework/Android.bp6
-rw-r--r--packages/CrashRecovery/framework/api/system-current.txt2
-rw-r--r--packages/CrashRecovery/framework/api/test-current.txt8
-rw-r--r--packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java69
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java156
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java4
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java13
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java9
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java4
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java4
-rw-r--r--packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_middle.xml2
-rw-r--r--packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml2
-rw-r--r--packages/CredentialManager/res/drawable/more_options_list_item.xml4
-rw-r--r--packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml4
-rw-r--r--packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml6
-rw-r--r--packages/CredentialManager/res/values-hy/strings.xml2
-rw-r--r--packages/CredentialManager/res/values/colors.xml8
-rw-r--r--packages/CredentialManager/wear/res/values-hi/strings.xml4
-rw-r--r--packages/CredentialManager/wear/res/values-ta/strings.xml2
-rw-r--r--packages/CredentialManager/wear/res/values-te/strings.xml2
-rw-r--r--packages/NeuralNetworks/framework/Android.bp41
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/DownloadCallback.java (renamed from core/java/android/app/ondeviceintelligence/DownloadCallback.java)9
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.aidl (renamed from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java)14
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java267
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl22
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java169
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl24
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IDownloadCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl79
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IProcessingSignal.aidl (renamed from core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl24
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IResponseCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IResponseCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl (renamed from core/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl22
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java (renamed from core/java/android/app/ondeviceintelligence/InferenceInfo.java)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java (renamed from core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java)8
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java54
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java677
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingCallback.java (renamed from core/java/android/app/ondeviceintelligence/ProcessingCallback.java)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingSignal.java (renamed from core/java/android/app/ondeviceintelligence/ProcessingSignal.java)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java (renamed from core/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl22
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.java (renamed from core/java/android/app/ondeviceintelligence/TokenInfo.java)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java96
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl51
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl53
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl (renamed from core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl (renamed from core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl34
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java676
-rw-r--r--packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java734
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java113
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl (renamed from core/java/android/app/ondeviceintelligence/Feature.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java (renamed from core/java/android/app/ondeviceintelligence/Feature.java)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl (renamed from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java (renamed from core/java/android/app/ondeviceintelligence/FeatureDetails.java)5
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl24
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl31
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl14
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl14
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl15
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl (renamed from core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl14
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl24
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl16
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl18
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl14
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl (renamed from core/java/android/app/ondeviceintelligence/InferenceInfo.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java220
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java200
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java54
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java (renamed from core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java)5
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java72
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java225
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java41
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl (renamed from core/java/android/app/ondeviceintelligence/TokenInfo.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java94
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java96
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl (renamed from core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl (renamed from core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl14
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl31
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl (renamed from core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl)0
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java (renamed from core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java)4
-rw-r--r--packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java (renamed from core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java)14
-rw-r--r--packages/NeuralNetworks/service/Android.bp38
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java415
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java104
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java40
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java1147
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java148
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java71
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java80
-rw-r--r--packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java99
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java (renamed from services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java)0
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java (renamed from services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java)0
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java40
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java (renamed from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java)22
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java (renamed from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java)0
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java (renamed from services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java)0
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java (renamed from services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java)0
-rw-r--r--packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java (renamed from services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java)0
-rw-r--r--packages/PackageInstaller/TEST_MAPPING11
-rw-r--r--packages/PackageInstaller/res/values-af/strings.xml8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java24
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml2
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml2
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml2
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml2
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml5
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml5
-rw-r--r--packages/SettingsLib/CardPreference/res/values/styles_expressive.xml6
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml25
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml2
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml6
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml2
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt31
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt61
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt86
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt14
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt4
-rw-r--r--packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java31
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt21
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt4
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt18
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt27
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt15
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml6
-rw-r--r--packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java4
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant12.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant17.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant22.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant24.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant4.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant6.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant87.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant92.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant94.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant96.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant98.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_check.xml (renamed from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_close.xml (renamed from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_switch_thumb_icon.xml (renamed from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_preference_text_frame.xml (renamed from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml)6
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout/settingslib_preference_category_no_title.xml (renamed from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml35
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml35
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml33
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night/colors.xml53
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml47
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/config.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml129
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v33/styles_expressive.xml306
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml47
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml45
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml275
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml4
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/attrs_expressive.xml (renamed from packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/colors.xml79
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/config.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/dimens_expressive.xml (renamed from packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml)0
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml253
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt14
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts22
-rw-r--r--packages/SettingsLib/Spa/gallery/build.gradle.kts1
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt21
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml8
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts6
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt70
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt6
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt4
-rw-r--r--packages/SettingsLib/Spa/testutils/build.gradle.kts4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt2
-rw-r--r--packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml4
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume.xml44
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml31
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml31
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml43
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml31
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml43
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml47
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml43
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml47
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml51
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml31
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml39
-rw-r--r--packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml43
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml11
-rw-r--r--packages/SettingsLib/res/values-uk/arrays.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values/arrays.xml21
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java415
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java75
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidInfo.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java36
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt21
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java140
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java148
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java318
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java17
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java9
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java13
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java20
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java8
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java198
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java77
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java28
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java8
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig10
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java586
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java101
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java13
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java2
-rw-r--r--packages/Shell/Android.bp6
-rw-r--r--packages/Shell/AndroidManifest.xml5
-rw-r--r--packages/Shell/OWNERS4
-rw-r--r--packages/Shell/res/values/defaults.xml5
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java590
-rw-r--r--packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java55
-rw-r--r--packages/Shell/tests/src/com/android/shell/UiBot.java26
-rw-r--r--packages/SimAppDialog/res/values-af/strings.xml4
-rw-r--r--packages/SystemUI/AndroidManifest.xml10
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig42
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java25
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt36
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt50
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt69
-rw-r--r--packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt94
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt533
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt57
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt2
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt523
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt76
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Color.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt36
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt200
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt66
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt65
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt23
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt28
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt33
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt29
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt32
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt15
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt35
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt83
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt107
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt141
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt48
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt34
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt36
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt24
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt173
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt100
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt10
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt5
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt19
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt12
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt134
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt292
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt175
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt4
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt35
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt32
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt6
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt7
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt1
-rw-r--r--packages/SystemUI/lint-baseline.xml988
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java227
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java278
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java273
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java187
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt154
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt222
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/OWNERS3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt125
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt212
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt137
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt87
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt65
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt74
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt53
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt68
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt164
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt)1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt107
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt105
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt84
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt59
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt81
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt81
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java68
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt172
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt314
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt149
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt1237
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt76
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt705
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt789
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt48
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt419
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java379
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt454
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java25
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt202
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt146
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt48
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt111
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt44
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt154
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt14
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java7
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt27
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt1
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java6
-rw-r--r--packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml2
-rw-r--r--packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml2
-rw-r--r--packages/SystemUI/res-keyguard/drawable/progress_bar.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml8
-rw-r--r--packages/SystemUI/res-product/values/strings.xml4
-rw-r--r--packages/SystemUI/res/color/connected_network_primary_color.xml2
-rw-r--r--packages/SystemUI/res/color/disconnected_network_primary_color.xml2
-rw-r--r--packages/SystemUI/res/color/menu_item_text.xml4
-rw-r--r--packages/SystemUI/res/color/notification_focus_overlay_color.xml2
-rw-r--r--packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml2
-rw-r--r--packages/SystemUI/res/color/notification_guts_priority_contents.xml2
-rw-r--r--packages/SystemUI/res/color/notification_state_color_default.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_filled_background.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_filled_text_color.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_outline.xml4
-rw-r--r--packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml4
-rw-r--r--packages/SystemUI/res/color/remote_input_hint.xml2
-rw-r--r--packages/SystemUI/res/color/remote_input_send.xml4
-rw-r--r--packages/SystemUI/res/color/remote_input_text.xml4
-rw-r--r--packages/SystemUI/res/color/screenshare_options_spinner_background.xml4
-rw-r--r--packages/SystemUI/res/color/slider_active_track_color.xml4
-rw-r--r--packages/SystemUI/res/color/slider_inactive_track_color.xml4
-rw-r--r--packages/SystemUI/res/color/slider_thumb_color.xml4
-rw-r--r--packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_down.xml4
-rw-r--r--packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_up.xml4
-rw-r--r--packages/SystemUI/res/drawable/action_chip_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/action_chip_container_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/biometric_prompt_vertical_list_content_view_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/brightness_bar.xml2
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_drawable.xml2
-rw-r--r--packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/chipbar_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/chipbar_end_button_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/hearing_devices_spinner_popup_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml (renamed from libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_button_dark.xml)9
-rw-r--r--packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_check_circle_filled_24dp.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_circle_outline_24dp.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_screensaver_auto.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_shortcutlist_search.xml2
-rw-r--r--packages/SystemUI/res/drawable/immersive_cling_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/notif_footer_btn_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/notification_guts_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/notification_material_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/overlay_border.xml2
-rw-r--r--packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_down.xml4
-rw-r--r--packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_up.xml4
-rw-r--r--packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_tile_focused_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/remote_input_view_text_bg.xml4
-rw-r--r--packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/screenshot_edit_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml2
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_on.xml2
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_on_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/shelf_action_chip_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/shortcut_button_colored.xml2
-rw-r--r--packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml2
-rw-r--r--packages/SystemUI/res/drawable/shortcut_search_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml2
-rw-r--r--packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_background_top.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_ringer_item_bg.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_row_seekbar.xml2
-rw-r--r--packages/SystemUI/res/layout/alert_dialog_title_systemui.xml2
-rw-r--r--packages/SystemUI/res/layout/app_clips_screenshot.xml10
-rw-r--r--packages/SystemUI/res/layout/bundle_notification_info.xml4
-rw-r--r--packages/SystemUI/res/layout/chipbar.xml8
-rw-r--r--packages/SystemUI/res/layout/clipboard_edit_text_activity.xml4
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml4
-rw-r--r--packages/SystemUI/res/layout/connected_display_dialog.xml4
-rw-r--r--packages/SystemUI/res/layout/contextual_edu_dialog.xml5
-rw-r--r--packages/SystemUI/res/layout/hearing_devices_spinner_dropdown_view.xml2
-rw-r--r--packages/SystemUI/res/layout/immersive_mode_cling.xml6
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml3
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml4
-rw-r--r--packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml4
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml24
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml2
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf_row.xml4
-rw-r--r--packages/SystemUI/res/layout/notification_2025_footer.xml (renamed from packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml)2
-rw-r--r--packages/SystemUI/res/layout/notification_children_divider.xml2
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml2
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml4
-rw-r--r--packages/SystemUI/res/layout/notification_snooze.xml4
-rw-r--r--packages/SystemUI/res/layout/notification_snooze_option.xml2
-rw-r--r--packages/SystemUI/res/layout/ongoing_activity_chip.xml12
-rw-r--r--packages/SystemUI/res/layout/privacy_dialog_item_v2.xml2
-rw-r--r--packages/SystemUI/res/layout/privacy_dialog_v2.xml2
-rw-r--r--packages/SystemUI/res/layout/record_issue_dialog.xml4
-rw-r--r--packages/SystemUI/res/layout/screen_record_options.xml4
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml2
-rw-r--r--packages/SystemUI/res/layout/screenshot_shelf.xml4
-rw-r--r--packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml4
-rw-r--r--packages/SystemUI/res/layout/shelf_action_chip.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider.xml22
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_button.xml2
-rw-r--r--packages/SystemUI/res/raw/trackpad_recent_apps_edu.json2
-rw-r--r--packages/SystemUI/res/raw/trackpad_recent_apps_success.json2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml88
-rw-r--r--packages/SystemUI/res/values-am/strings.xml40
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml22
-rw-r--r--packages/SystemUI/res/values-as/strings.xml39
-rw-r--r--packages/SystemUI/res/values-az/strings.xml40
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml39
-rw-r--r--packages/SystemUI/res/values-be/strings.xml40
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml39
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml40
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml39
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml39
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml41
-rw-r--r--packages/SystemUI/res/values-da/strings.xml40
-rw-r--r--packages/SystemUI/res/values-de/strings.xml40
-rw-r--r--packages/SystemUI/res/values-el/strings.xml39
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml40
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml18
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml40
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml40
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml41
-rw-r--r--packages/SystemUI/res/values-es/strings.xml42
-rw-r--r--packages/SystemUI/res/values-et/strings.xml40
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml40
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml40
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml40
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml40
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml40
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml40
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml40
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml41
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml41
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml40
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml40
-rw-r--r--packages/SystemUI/res/values-in/strings.xml40
-rw-r--r--packages/SystemUI/res/values-is/strings.xml21
-rw-r--r--packages/SystemUI/res/values-it/strings.xml18
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml40
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml21
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml40
-rw-r--r--packages/SystemUI/res/values-km/strings.xml41
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml40
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml22
-rw-r--r--packages/SystemUI/res/values-land/styles.xml6
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml21
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml21
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml40
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml45
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml39
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml40
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml24
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml39
-rw-r--r--packages/SystemUI/res/values-my/strings.xml39
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml40
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml39
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml39
-rw-r--r--packages/SystemUI/res/values-or/strings.xml39
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml40
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml41
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml44
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml44
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml40
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml40
-rw-r--r--packages/SystemUI/res/values-si/strings.xml40
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml42
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml21
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml44
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml39
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml41
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml40
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/styles.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/styles.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/styles.xml6
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/styles.xml2
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml42
-rw-r--r--packages/SystemUI/res/values-te/strings.xml39
-rw-r--r--packages/SystemUI/res/values-th/strings.xml39
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml39
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml41
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml41
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml39
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml40
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml40
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml39
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml43
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml40
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml41
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml45
-rw-r--r--packages/SystemUI/res/values/styles.xml155
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/EventLogTags.logtags2
-rw-r--r--packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/FontStyles.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/OWNERS3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java143
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java193
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesSpinnerAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Color.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/OWNERS3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt147
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt280
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt300
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHost.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt140
-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/toolbar/EditModeButton.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt111
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl (renamed from packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl (renamed from packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt (renamed from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt (renamed from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt)9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt (renamed from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt (renamed from packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt (renamed from packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt115
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java304
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt180
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt128
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonUiModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt471
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt410
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt115
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt50
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt)3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt)3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt38
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt5
-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/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt47
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt45
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt21
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt9
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt7
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt18
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt58
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt23
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt44
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt10
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt6
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt14
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt31
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt22
-rw-r--r--packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt24
-rw-r--r--packages/Vcn/flags/Android.bp14
-rw-r--r--packages/Vcn/framework-b/Android.bp98
-rw-r--r--packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt2
-rw-r--r--packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt4
-rw-r--r--packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java24
-rw-r--r--packages/Vcn/service-b/Android.bp57
-rw-r--r--packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt5
-rw-r--r--packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/VcnManagementService.java9
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java14
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java4
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java4
-rw-r--r--proto/Android.bp4
-rw-r--r--ravenwood/Android.bp16
-rw-r--r--ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java54
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java12
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java13
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java14
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java4
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java4
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java6
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java139
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java4
-rw-r--r--ravenwood/runtime-jni/ravenwood_initializer.cpp4
-rw-r--r--ravenwood/runtime-jni/ravenwood_runtime.cpp2
-rwxr-xr-xravenwood/scripts/extract-last-soong-commands.py4
-rw-r--r--ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java42
-rw-r--r--services/Android.bp35
-rw-r--r--services/appfunctions/Android.bp8
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java7
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java54
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java83
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java8
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java36
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java3
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java28
-rw-r--r--services/art-wear-profile2
-rw-r--r--services/autofill/bugfixes.aconfig20
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java26
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java30
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java45
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java320
-rw-r--r--services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java5
-rw-r--r--services/companion/java/com/android/server/companion/BackupRestoreProcessor.java6
-rw-r--r--services/companion/java/com/android/server/companion/utils/MetricUtils.java6
-rw-r--r--services/companion/java/com/android/server/companion/utils/PermissionsUtils.java3
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java8
-rw-r--r--services/core/Android.bp8
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java29
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java230
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java47
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java79
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java6
-rw-r--r--services/core/java/com/android/server/am/BroadcastController.java3
-rw-r--r--services/core/java/com/android/server/am/BroadcastFilter.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java20
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java65
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java18
-rw-r--r--services/core/java/com/android/server/app/GameManagerSettings.java8
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java101
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java3
-rw-r--r--services/core/java/com/android/server/appop/AttributedOp.java10
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java141
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java2
-rw-r--r--services/core/java/com/android/server/content/SyncLogger.java4
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java62
-rw-r--r--services/core/java/com/android/server/display/BrightnessRangeController.java29
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java87
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerProximityStateController.java57
-rw-r--r--services/core/java/com/android/server/display/ExternalDisplayPolicy.java6
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java60
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java8
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java38
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessEvent.java22
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java12
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java25
-rw-r--r--services/core/java/com/android/server/display/color/DisplayTransformManager.java6
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java28
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java5
-rw-r--r--services/core/java/com/android/server/display/mode/ModeChangeObserver.java108
-rw-r--r--services/core/java/com/android/server/display/mode/RejectedModesVote.java40
-rw-r--r--services/core/java/com/android/server/display/mode/Vote.java43
-rw-r--r--services/core/java/com/android/server/display/mode/VoteSummary.java26
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginEventStorage.java134
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginStorage.java10
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java17
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java3
-rw-r--r--services/core/java/com/android/server/input/InputDataStore.java344
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java117
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java69
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java10
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java253
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java253
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java62
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java30
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java35
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java70
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java93
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java22
-rw-r--r--services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java169
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java99
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudger.java58
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudgerCache.java7
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java8
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java38
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java28
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java36
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java78
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java147
-rw-r--r--services/core/java/com/android/server/media/quality/BiMap.java119
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java233
-rw-r--r--services/core/java/com/android/server/media/quality/OWNERS3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java1
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java32
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/OWNERS1
-rw-r--r--services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java118
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java85
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java11
-rw-r--r--services/core/java/com/android/server/pm/DistractingPackageHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/InstallDependencyHelper.java42
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java16
-rw-r--r--services/core/java/com/android/server/pm/InstallingSession.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java38
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java35
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageMetrics.java55
-rw-r--r--services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java87
-rw-r--r--services/core/java/com/android/server/pm/RestrictionsSet.java8
-rw-r--r--services/core/java/com/android/server/pm/SaferIntentUtils.java8
-rw-r--r--services/core/java/com/android/server/pm/StorageEventHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java16
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING16
-rw-r--r--services/core/java/com/android/server/pm/UserDataPreparer.java29
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java12
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java11
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java12
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java13
-rw-r--r--services/core/java/com/android/server/policy/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java87
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java4
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java129
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java23
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java43
-rw-r--r--services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java25
-rw-r--r--services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java25
-rw-r--r--services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java14
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java12
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java63
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java58
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java33
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/DataSource.java5
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java42
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java87
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java14
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java177
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java21
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java50
-rw-r--r--services/core/java/com/android/server/telecom/TelecomLoaderService.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java26
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java86
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotController.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraPolicy.java11
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java11
-rw-r--r--services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java29
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java23
-rw-r--r--services/core/java/com/android/server/wm/DeferredDisplayUpdater.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java59
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java6
-rw-r--r--services/core/java/com/android/server/wm/DragState.java2
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java3
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java3
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java18
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java99
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java2
-rw-r--r--services/core/java/com/android/server/wm/SnapshotCache.java2
-rw-r--r--services/core/java/com/android/server/wm/SnapshotController.java2
-rw-r--r--services/core/java/com/android/server/wm/SnapshotPersistQueue.java9
-rw-r--r--services/core/java/com/android/server/wm/Task.java77
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java77
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java301
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotCache.java39
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java32
-rw-r--r--services/core/java/com/android/server/wm/Transition.java12
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContextListenerController.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerConstants.java34
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java123
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java112
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java13
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/core/jni/com_android_server_UsbAlsaDevice.cpp70
-rw-r--r--services/core/jni/com_android_server_am_Freezer.cpp2
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp16
-rw-r--r--services/core/jni/onload.cpp4
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java26
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java56
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java20
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java8
-rw-r--r--services/java/com/android/server/SystemServer.java35
-rw-r--r--services/java/com/android/server/flags.aconfig8
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java3
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt44
-rw-r--r--services/proguard.flags3
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java48
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java7
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java94
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java24
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java65
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java20
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java31
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java38
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java23
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/RejectedModesVoteTest.kt58
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt38
-rw-r--r--services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java19
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java80
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java53
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java45
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java43
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java31
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt22
-rw-r--r--services/tests/ondeviceintelligencetests/Android.bp8
-rw-r--r--services/tests/ondeviceintelligencetests/OWNERS4
-rw-r--r--services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java11
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java146
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java205
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java8
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java12
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java12
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java6
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java28
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java2
-rw-r--r--services/tests/security/intrusiondetection/AndroidManifest.xml10
-rw-r--r--services/tests/security/intrusiondetection/AndroidTest.xml4
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java81
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml4
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java549
-rw-r--r--services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java216
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java172
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt35
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/OWNERS1
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java29
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java92
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java31
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java42
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java39
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java35
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java32
-rw-r--r--services/tests/wmtests/res/xml/bookmarks.xml14
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java164
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java61
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java122
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java184
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java232
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java94
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java88
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java151
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java39
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java30
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaDevice.java6
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java9
-rw-r--r--services/usb/java/com/android/server/usb/flags/usb_flags.aconfig7
-rw-r--r--telecomm/java/android/telecom/ParcelableCallAnalytics.java7
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java9
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java104
-rw-r--r--telephony/java/android/telephony/CellBroadcastService.java16
-rw-r--r--telephony/java/android/telephony/CellIdentityCdma.java115
-rw-r--r--telephony/java/android/telephony/CellInfoCdma.java26
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthCdma.java29
-rw-r--r--telephony/java/android/telephony/PreciseDisconnectCause.java70
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java72
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java256
-rw-r--r--telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java88
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java6
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java8
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java50
-rw-r--r--telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java7
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java13
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl47
-rw-r--r--tests/AppJankTest/Android.bp1
-rw-r--r--tests/AppJankTest/AndroidManifest.xml19
-rw-r--r--tests/AppJankTest/res/layout/jank_tracker_activity_layout.xml47
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java215
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java32
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankTrackerActivity.java32
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankUtils.java52
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/TestWidget.java69
-rw-r--r--tests/Input/res/xml/bookmarks.xml16
-rw-r--r--tests/Input/res/xml/bookmarks_legacy.xml14
-rw-r--r--tests/Input/src/com/android/server/input/InputDataStoreTests.kt504
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt129
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt171
-rw-r--r--tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt55
-rw-r--r--tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml38
-rw-r--r--tests/NetworkSecurityConfigTest/res/xml/ct_users.xml15
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java15
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java43
-rw-r--r--tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java30
-rw-r--r--tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java6
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java230
-rw-r--r--tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java19
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java24
-rw-r--r--tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java6
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt3
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt8
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt8
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt3
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt2
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt3
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt16
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt12
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt22
-rw-r--r--tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt128
-rw-r--r--tools/systemfeatures/tests/src/ArraySet.java34
-rw-r--r--tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java19
2371 files changed, 79182 insertions, 33797 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 371177c8b0a5..97d28d1a6506 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -95,6 +95,7 @@ aconfig_declarations_group {
"com.android.internal.foldables.flags-aconfig-java",
"com.android.internal.os.flags-aconfig-java",
"com.android.internal.pm.pkg.component.flags-aconfig-java",
+ "com.android.internal.widget.flags-aconfig-java",
"com.android.media.flags.bettertogether-aconfig-java",
"com.android.media.flags.editing-aconfig-java",
"com.android.media.flags.performance-aconfig-java",
@@ -279,6 +280,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+aconfig_declarations {
+ name: "com.android.internal.widget.flags-aconfig",
+ package: "com.android.internal.widget.flags",
+ container: "system",
+ srcs: ["core/java/com/android/internal/widget/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.internal.widget.flags-aconfig-java",
+ aconfig_declarations: "com.android.internal.widget.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Text
aconfig_declarations {
name: "com.android.text.flags-aconfig",
@@ -387,6 +401,7 @@ java_aconfig_library {
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
+ "com.android.tethering",
"com.android.wifi",
],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
@@ -622,6 +637,11 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "aconfig_hardware_flags_c_lib",
+ aconfig_declarations: "android.hardware.flags-aconfig",
+}
+
// Widget
aconfig_declarations {
name: "android.widget.flags-aconfig",
@@ -789,21 +809,6 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
-// OnDeviceIntelligence
-aconfig_declarations {
- name: "android.app.ondeviceintelligence-aconfig",
- exportable: true,
- package: "android.app.ondeviceintelligence.flags",
- container: "system",
- srcs: ["core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig"],
-}
-
-java_aconfig_library {
- name: "android.app.ondeviceintelligence-aconfig-java",
- aconfig_declarations: "android.app.ondeviceintelligence-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
-}
-
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
@@ -985,6 +990,11 @@ aconfig_declarations {
java_aconfig_library {
name: "android.app.flags-aconfig-java",
aconfig_declarations: "android.app.flags-aconfig",
+ min_sdk_version: "34",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -1206,6 +1216,7 @@ java_aconfig_library {
// DevicePolicy
aconfig_declarations {
name: "device_policy_aconfig_flags",
+ exportable: true,
package: "android.app.admin.flags",
container: "system",
srcs: [
@@ -1224,6 +1235,7 @@ java_aconfig_library {
aconfig_declarations: "device_policy_aconfig_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
min_sdk_version: "30",
+ mode: "exported",
apex_available: [
"//apex_available:platform",
"com.android.permission",
@@ -1458,6 +1470,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.appwidget.flags-aconfig-java-host",
+ aconfig_declarations: "android.appwidget.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// App
aconfig_declarations {
name: "android.server.app.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index a525583b8ba0..529da53e58f7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -446,6 +446,9 @@ java_library {
default: [
"framework-platformcrashrecovery.impl",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [],
+ default: ["framework-ondeviceintelligence-platform.impl"],
}),
sdk_version: "core_platform",
installable: false,
@@ -489,6 +492,7 @@ java_library {
apex_available: ["//apex_available:platform"],
visibility: [
"//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
],
compile_dex: false,
headers_only: true,
@@ -584,6 +588,9 @@ java_library {
default: [
"framework-platformcrashrecovery-compat-config",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [],
+ default: ["framework-ondeviceintelligence-platform-compat-config"],
}),
}
diff --git a/OWNERS b/OWNERS
index d0a634e529c5..058ea3619a58 100644
--- a/OWNERS
+++ b/OWNERS
@@ -34,6 +34,8 @@ per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
per-file *ravenwood* = file:ravenwood/OWNERS
per-file *Ravenwood* = file:ravenwood/OWNERS
+per-file PREUPLOAD.cfg = file:/PREUPLOAD_OWNERS
+
per-file INPUT_OWNERS = file:/INPUT_OWNERS
per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
@@ -48,3 +50,4 @@ per-file BROADCASTS_OWNERS = file:/BROADCASTS_OWNERS
per-file ADPF_OWNERS = file:/ADPF_OWNERS
per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS
per-file SDK_OWNERS = file:/SDK_OWNERS
+per-file PREUPLOAD_OWNERS = file:/PREUPLOAD_OWNERS
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index d83109a1a986..5e0428bab467 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -18,7 +18,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
tests/
tools/
bpfmt = -d
-ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/PREUPLOAD_OWNERS b/PREUPLOAD_OWNERS
new file mode 100644
index 000000000000..ece4d3e5e268
--- /dev/null
+++ b/PREUPLOAD_OWNERS
@@ -0,0 +1,2 @@
+roosa@google.com
+gsennton@google.com
diff --git a/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java
new file mode 100644
index 000000000000..205c7b875bbc
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.content.pm;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SystemFeaturesMetadataPerfTest {
+ // As each query is relatively cheap, add an inner iteration loop to reduce execution noise.
+ private static final int NUM_ITERATIONS = 10;
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void maybeGetSdkFeatureIndex_featureDefined() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_WATCH);
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_LEANBACK);
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_IPSEC_TUNNELS);
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_WEBVIEW);
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_NFC_BEAM);
+ PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_AUTOFILL);
+ }
+ }
+ }
+
+ @Test
+ public void maybeGetSdkFeatureIndex_featureUndefined() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ PackageManager.maybeGetSdkFeatureIndex("com.android.custom.feature.1");
+ PackageManager.maybeGetSdkFeatureIndex("com.android.custom.feature.2");
+ PackageManager.maybeGetSdkFeatureIndex("foo");
+ PackageManager.maybeGetSdkFeatureIndex("bar");
+ PackageManager.maybeGetSdkFeatureIndex("0");
+ PackageManager.maybeGetSdkFeatureIndex("");
+ }
+ }
+ }
+
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index ed669beae1ce..c77528021201 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -63,14 +63,12 @@ public class ZipFilePerfTest {
@Test
@Parameters(method = "getData")
- public void timeZipFileOpen(int numEntries) throws Exception {
+ public void timeZipFileOpenClose(int numEntries) throws Exception {
setUp(numEntries);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
ZipFile zf = new ZipFile(mFile);
- state.pauseTiming();
zf.close();
- state.resumeTiming();
}
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index 73bff08c626d..af0237491639 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
import android.os.Debug;
+import android.os.Trace;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -129,17 +130,23 @@ public final class BenchmarkState {
}
private void beginWarmup() {
+ Trace.beginSection("Warmup");
mStartTimeNs = System.nanoTime();
mIteration = 0;
mState = WARMUP;
}
+ private void endWarmup() {
+ Trace.endSection();
+ }
+
private void beginBenchmark(long warmupDuration, int iterations) {
if (ENABLE_PROFILING) {
File f = new File(InstrumentationRegistry.getContext().getDataDir(), "benchprof");
Log.d(TAG, "Tracing to: " + f.getAbsolutePath());
Debug.startMethodTracingSampling(f.getAbsolutePath(), 16 * 1024 * 1024, 100);
}
+ Trace.beginSection("Benchmark");
mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
@@ -150,6 +157,10 @@ public final class BenchmarkState {
mStartTimeNs = System.nanoTime();
}
+ private void endBenchmark() {
+ Trace.endSection();
+ }
+
private boolean startNextTestRun() {
final long currentTime = System.nanoTime();
mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
@@ -165,6 +176,7 @@ public final class BenchmarkState {
return true;
}
mState = FINISHED;
+ endBenchmark();
return false;
}
mPausedDurationNs = 0;
@@ -189,6 +201,7 @@ public final class BenchmarkState {
// don't yet have a target iteration count.
final long duration = System.nanoTime() - mStartTimeNs;
if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
+ endWarmup();
beginBenchmark(duration, mIteration);
}
return true;
@@ -208,6 +221,7 @@ public final class BenchmarkState {
mCustomizedIterations++;
if (mCustomizedIterations >= mMaxCustomizedIterations) {
mState = FINISHED;
+ endBenchmark();
return false;
}
mCustomizedIterationListener.onStart(mCustomizedIterations);
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 810be8fc4220..86ed06bf4e3d 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -63,7 +63,7 @@ flag {
name: "remove_user_during_user_switch"
namespace: "backstage_power"
description: "Remove started user if user will be stopped due to user switch"
- bug: "321598070"
+ bug: "337077643"
}
flag {
@@ -95,4 +95,14 @@ flag {
namespace: "backstage_power"
description: "Apply the quota policy to jobs started when the app was in TOP state"
bug: "374323858"
+}
+
+flag {
+ name: "enforce_schedule_limit_to_proxy_jobs"
+ namespace: "backstage_power"
+ description: "Limit the schedule calls towards the persisted proxy jobs"
+ bug: "377912323"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index a5a08fb9997c..0b884057ea19 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1718,8 +1718,9 @@ public class JobSchedulerService extends com.android.server.SystemService
int userId, @Nullable String namespace, String tag) {
// Rate limit excessive schedule() calls.
final String servicePkg = job.getService().getPackageName();
- if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
- // Only limit schedule calls for persisted jobs scheduled by the app itself.
+ if (job.isPersisted() && (Flags.enforceScheduleLimitToProxyJobs()
+ || (packageName == null || packageName.equals(servicePkg)))) {
+ // limit excessive schedule calls for persisted jobs.
final String pkg = packageName == null ? servicePkg : packageName;
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
@@ -1981,7 +1982,12 @@ public class JobSchedulerService extends com.android.server.SystemService
jobStatus.getNumAppliedFlexibleConstraints(),
jobStatus.getNumDroppedFlexibleConstraints(),
jobStatus.getFilteredTraceTag(),
- jobStatus.getFilteredDebugTags());
+ jobStatus.getFilteredDebugTags(),
+ jobStatus.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ jobStatus.getJob().getBackoffPolicy() + 1,
+ shouldUseAggressiveBackoff(jobStatus.getNumAbandonedFailures()));
+
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2422,7 +2428,11 @@ public class JobSchedulerService extends com.android.server.SystemService
cancelled.getNumAppliedFlexibleConstraints(),
cancelled.getNumDroppedFlexibleConstraints(),
cancelled.getFilteredTraceTag(),
- cancelled.getFilteredDebugTags());
+ cancelled.getFilteredDebugTags(),
+ cancelled.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ cancelled.getJob().getBackoffPolicy() + 1,
+ shouldUseAggressiveBackoff(cancelled.getNumAbandonedFailures()));
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
@@ -5917,9 +5927,6 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(Flags.FLAG_DO_NOT_FORCE_RUSH_EXECUTION_AT_BOOT,
Flags.doNotForceRushExecutionAtBoot());
pw.println();
- pw.print(android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION,
- android.app.job.Flags.backupJobsExemption());
- pw.println();
pw.print(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND,
android.app.job.Flags.ignoreImportantWhileForeground());
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index f3bc9c747f17..42c8250a6185 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -433,9 +433,6 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
case com.android.server.job.Flags.FLAG_DO_NOT_FORCE_RUSH_EXECUTION_AT_BOOT:
pw.println(com.android.server.job.Flags.doNotForceRushExecutionAtBoot());
break;
- case android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION:
- pw.println(android.app.job.Flags.backupJobsExemption());
- break;
case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 909a9b30ada4..2b401c8ff6b1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -546,7 +546,11 @@ public final class JobServiceContext implements ServiceConnection {
job.getNumAppliedFlexibleConstraints(),
job.getNumDroppedFlexibleConstraints(),
job.getFilteredTraceTag(),
- job.getFilteredDebugTags());
+ job.getFilteredDebugTags(),
+ job.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ job.getJob().getBackoffPolicy() + 1,
+ mService.shouldUseAggressiveBackoff(job.getNumAbandonedFailures()));
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1681,7 +1685,11 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.getNumAppliedFlexibleConstraints(),
completedJob.getNumDroppedFlexibleConstraints(),
completedJob.getFilteredTraceTag(),
- completedJob.getFilteredDebugTags());
+ completedJob.getFilteredDebugTags(),
+ completedJob.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ completedJob.getJob().getBackoffPolicy() + 1,
+ mService.shouldUseAggressiveBackoff(completedJob.getNumAbandonedFailures()));
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
JobSchedulerService.TRACE_TRACK_NAME, getId());
diff --git a/api/Android.bp b/api/Android.bp
index 73262030ee37..14c2766d8887 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -105,6 +105,13 @@ combined_apis {
default: [
"framework-platformcrashrecovery",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [
+ "framework-ondeviceintelligence",
+ ],
+ default: [
+ "framework-ondeviceintelligence-platform",
+ ],
}) + select(release_flag("RELEASE_RANGING_STACK"), {
true: [
"framework-ranging",
@@ -119,7 +126,12 @@ combined_apis {
"service-permission",
"service-rkp",
"service-sdksandbox",
- ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ ] + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [
+ "service-ondeviceintelligence",
+ ],
+ default: [],
+ }) + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": [
"service-crashrecovery",
],
@@ -478,6 +490,7 @@ java_defaults {
"//frameworks/base/location",
"//frameworks/base/packages/CrashRecovery/framework",
"//frameworks/base/nfc",
+ "//packages/modules/NeuralNetworks:__subpackages__",
],
plugins: ["error_prone_android_framework"],
errorprone: {
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 89351fd47ff8..03fb44fd8145 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -130,6 +130,10 @@ droidstubs {
droidstubs {
name: "framework-doc-stubs",
defaults: ["android-non-updatable-doc-stubs-defaults"],
+ flags: [
+ // Ignore any compatibility errors, see check_api.last_released below for more information.
+ "--hide-category Compatibility",
+ ],
srcs: [":all-modules-public-stubs-source-exportable"],
api_levels_module: "api_versions_public",
aidl: {
@@ -138,13 +142,39 @@ droidstubs {
"packages/modules/Media/apex/aidl/stable",
],
},
+
+ // Pass the previously released API to support reverting flagged APIs. Without this, reverting
+ // a flagged API will cause it to be removed, even if it had previously been released. This
+ // has the side effect of causing compatibility issues to be reported but they are already
+ // checked elsewhere so they will be ignored, see `--hide-category Compatibility` above.
+ check_api: {
+ last_released: {
+ api_file: ":android.api.combined.public.latest",
+ removed_api_file: ":android-removed.api.combined.public.latest",
+ },
+ },
}
droidstubs {
name: "framework-doc-system-stubs",
defaults: ["framework-doc-stubs-sources-default"],
- flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
+ flags: [
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+ // Ignore any compatibility errors, see check_api.last_released below for more information.
+ "--hide-category Compatibility",
+ ],
api_levels_module: "api_versions_system",
+
+ // Pass the previously released API to support reverting flagged APIs. Without this, reverting
+ // a flagged API will cause it to be removed, even if it had previously been released. This
+ // has the side effect of causing compatibility issues to be reported but they are already
+ // checked elsewhere so they will be ignored, see `--hide-category Compatibility` above.
+ check_api: {
+ last_released: {
+ api_file: ":android.api.combined.system.latest",
+ removed_api_file: ":android-removed.api.combined.system.latest",
+ },
+ },
}
/////////////////////////////////////////////////////////////////////
diff --git a/api/api.go b/api/api.go
index 5ca24de1b46a..e4d783eba4c3 100644
--- a/api/api.go
+++ b/api/api.go
@@ -29,6 +29,7 @@ const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
const location = "framework-location"
const platformCrashrecovery = "framework-platformcrashrecovery"
+const ondeviceintelligence = "framework-ondeviceintelligence-platform"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -40,7 +41,7 @@ var core_libraries_modules = []string{art, conscrypt, i18n}
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization, location, platformCrashrecovery}
+var non_updatable_modules = []string{virtualization, location, platformCrashrecovery, ondeviceintelligence}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
diff --git a/boot/Android.bp b/boot/Android.bp
index 6eead42a4d30..eaa984ac0cdd 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -31,6 +31,7 @@ soong_config_module_type {
"car_bootclasspath_fragment",
"nfc_apex_bootclasspath_fragment",
"release_crashrecovery_module",
+ "release_ondevice_intelligence_module",
"release_package_profiling_module",
],
properties: [
@@ -176,6 +177,15 @@ custom_platform_bootclasspath {
},
],
},
+ release_ondevice_intelligence_module: {
+ fragments: [
+ // only used when ondeviceintelligence is moved to neuralnetworks module
+ {
+ apex: "com.android.neuralnetworks",
+ module: "com.android.ondeviceintelligence-bootclasspath-fragment",
+ },
+ ],
+ },
release_package_profiling_module: {
fragments: [
// only used if profiling is enabled.
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index a696e03d5bdf..afd9984cb124 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -6469,6 +6469,7 @@ android.os.connectivity.WifiActivityEnergyInfo$1
android.os.connectivity.WifiActivityEnergyInfo
android.os.connectivity.WifiBatteryStats$1
android.os.connectivity.WifiBatteryStats
+android.os.flagging.AconfigPackage
android.os.health.HealthKeys$Constant
android.os.health.HealthKeys$Constants
android.os.health.HealthKeys$SortedIntArray
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 12de82a46263..d563ad3fd3db 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -416,7 +416,6 @@ int main(int argc, char** argv)
format = ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
} else if (jpeg) {
format = ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
- captureArgs.attachGainmap = true;
}
// setThreadPoolMaxThreadCount(0) actually tells the kernel it's
diff --git a/config/preloaded-classes b/config/preloaded-classes
index ed402767ee64..343de0bf3b98 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6473,6 +6473,7 @@ android.os.connectivity.WifiActivityEnergyInfo$1
android.os.connectivity.WifiActivityEnergyInfo
android.os.connectivity.WifiBatteryStats$1
android.os.connectivity.WifiBatteryStats
+android.os.flagging.AconfigPackage
android.os.health.HealthKeys$Constant
android.os.health.HealthKeys$Constants
android.os.health.HealthKeys$SortedIntArray
diff --git a/core/api/current.txt b/core/api/current.txt
index ac6e293c1729..ca4b2fae2f99 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -98,6 +98,7 @@ package android {
field public static final String DUMP = "android.permission.DUMP";
field public static final String ENFORCE_UPDATE_OWNERSHIP = "android.permission.ENFORCE_UPDATE_OWNERSHIP";
field public static final String EXECUTE_APP_ACTION = "android.permission.EXECUTE_APP_ACTION";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS";
field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";
field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
@@ -140,6 +141,7 @@ package android {
field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL";
field public static final String MANAGE_DEVICE_POLICY_AIRPLANE_MODE = "android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE";
field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String MANAGE_DEVICE_POLICY_APP_FUNCTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS";
field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
field public static final String MANAGE_DEVICE_POLICY_APP_USER_DATA = "android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA";
field public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
@@ -279,6 +281,7 @@ package android {
field public static final String REQUEST_COMPANION_PROFILE_COMPUTER = "android.permission.REQUEST_COMPANION_PROFILE_COMPUTER";
field public static final String REQUEST_COMPANION_PROFILE_GLASSES = "android.permission.REQUEST_COMPANION_PROFILE_GLASSES";
field public static final String REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING";
+ field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") public static final String REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING";
field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH";
field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND";
field public static final String REQUEST_COMPANION_SELF_MANAGED = "android.permission.REQUEST_COMPANION_SELF_MANAGED";
@@ -1504,7 +1507,6 @@ package android {
field public static final int shadowRadius = 16843108; // 0x1010164
field public static final int shape = 16843162; // 0x101019a
field public static final int shareInterpolator = 16843195; // 0x10101bb
- field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int shareRolePriority;
field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b
field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
@@ -1869,6 +1871,7 @@ package android {
field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
+ field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority;
field public static final int webTextViewStyle = 16843449; // 0x10102b9
field public static final int webViewStyle = 16842885; // 0x1010085
field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -2200,6 +2203,11 @@ package android {
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardFastSpatialDamping;
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardSlowEffectDamping;
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardSlowSpatialDamping;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusLarge;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusMedium;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusSmall;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusXlarge;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusXsmall;
field public static final int dialog_min_width_major = 17104899; // 0x1050003
field public static final int dialog_min_width_minor = 17104900; // 0x1050004
field public static final int notification_large_icon_height = 17104902; // 0x1050006
@@ -6476,6 +6484,7 @@ package android.app {
method public String getSortKey();
method public long getTimeoutAfter();
method public boolean hasImage();
+ method @FlaggedApi("android.app.api_rich_ongoing") public boolean hasPromotableCharacteristics();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final int BADGE_ICON_LARGE = 2; // 0x2
@@ -8074,6 +8083,7 @@ package android.app.admin {
field public static final String ACCOUNT_MANAGEMENT_DISABLED_POLICY = "accountManagementDisabled";
field public static final String APPLICATION_HIDDEN_POLICY = "applicationHidden";
field public static final String APPLICATION_RESTRICTIONS_POLICY = "applicationRestrictions";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String APP_FUNCTIONS_POLICY = "appFunctions";
field public static final String AUTO_TIMEZONE_POLICY = "autoTimezone";
field public static final String AUTO_TIME_POLICY = "autoTime";
field public static final String BACKUP_SERVICE_POLICY = "backupService";
@@ -8123,6 +8133,7 @@ package android.app.admin {
method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName);
method @Nullable public java.util.Set<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional=true) public int getAppFunctionsPolicy();
method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@Nullable android.content.ComponentName);
@@ -8281,6 +8292,7 @@ package android.app.admin {
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.Set<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional=true) public void setAppFunctionsPolicy(int);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE, conditional=true) public boolean setApplicationHidden(@Nullable android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8407,6 +8419,9 @@ package android.app.admin {
field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_DISABLED = 1; // 0x1
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_DISABLED_CROSS_PROFILE = 2; // 0x2
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_DISABLED = 1; // 0x1
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_ENABLED = 2; // 0x2
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
@@ -8871,14 +8886,15 @@ package android.app.appfunctions {
field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final int ERROR_DENIED = 1000; // 0x3e8
field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002; // 0x7d2
field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
}
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
- method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
- method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+ method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
+ method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
@@ -10051,6 +10067,7 @@ package android.companion {
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER) public static final String DEVICE_PROFILE_COMPUTER = "android.app.role.COMPANION_DEVICE_COMPUTER";
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES) public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING) public static final String DEVICE_PROFILE_NEARBY_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING";
+ field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING) public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
field public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
}
@@ -13366,6 +13383,7 @@ package android.content.pm {
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
method @NonNull public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence, @NonNull android.os.UserHandle);
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") @NonNull public static android.content.pm.SigningInfo getVerifiedSigningInfo(@NonNull String, int) throws android.content.pm.SigningInfoException;
method @NonNull @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(@NonNull String, int);
method @Nullable public abstract android.content.res.XmlResourceParser getXml(@NonNull String, @XmlRes int, @Nullable android.content.pm.ApplicationInfo);
method public boolean hasSigningCertificate(@NonNull String, @NonNull byte[], int);
@@ -14011,8 +14029,17 @@ package android.content.pm {
method public android.content.pm.Signature[] getSigningCertificateHistory();
method public boolean hasMultipleSigners();
method public boolean hasPastSigningCertificates();
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") public boolean signersMatchExactly(@NonNull android.content.pm.SigningInfo);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_JAR = 1; // 0x1
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V2 = 2; // 0x2
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V3 = 3; // 0x3
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V4 = 4; // 0x4
+ }
+
+ @FlaggedApi("android.content.pm.cloud_compilation_pm") public class SigningInfoException extends java.lang.Exception {
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") public int getCode();
}
public final class VersionedPackage implements android.os.Parcelable {
@@ -21781,6 +21808,18 @@ package android.media {
field public static final int ENCODING_DTS_UHD_P2 = 30; // 0x1e
field public static final int ENCODING_E_AC3 = 6; // 0x6
field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC = 42; // 0x2a
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC = 43; // 0x2b
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS = 41; // 0x29
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM = 44; // 0x2c
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_AAC = 38; // 0x26
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_FLAC = 39; // 0x27
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_OPUS = 37; // 0x25
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_PCM = 40; // 0x28
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_AAC = 34; // 0x22
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_FLAC = 35; // 0x23
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_OPUS = 33; // 0x21
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_PCM = 36; // 0x24
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -24902,7 +24941,8 @@ package android.media {
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
- method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.Set<java.lang.String> getRequiredPermissions();
+ method @FlaggedApi("com.android.media.flags.enable_media_route_2_info_provider_package_name") @Nullable public String getProviderPackageName();
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.List<java.util.Set<java.lang.String>> getRequiredPermissions();
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes();
method public int getType();
@@ -24972,6 +25012,7 @@ package android.media {
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.Set<java.lang.String>);
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.List<java.util.Set<java.lang.String>>);
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") @NonNull public android.media.MediaRoute2Info.Builder setSupportedRoutingTypes(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
@@ -27143,6 +27184,15 @@ package android.media.projection {
package android.media.quality {
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class ActiveProcessingPicture implements android.os.Parcelable {
+ ctor public ActiveProcessingPicture(int, @NonNull String);
+ method public int describeContents();
+ method public int getId();
+ method @NonNull public String getProfileId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.ActiveProcessingPicture> CREATOR;
+ }
+
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class AmbientBacklightEvent implements android.os.Parcelable {
ctor public AmbientBacklightEvent(int, @Nullable android.media.quality.AmbientBacklightMetadata);
method public int describeContents();
@@ -27194,8 +27244,30 @@ package android.media.quality {
}
public static final class MediaQualityContract.PictureQuality {
+ field public static final String PARAMETER_AUTO_PICTURE_QUALITY_ENABLED = "auto_picture_quality_enabled";
+ field public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED = "auto_super_resolution_enabled";
+ field public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
field public static final String PARAMETER_BRIGHTNESS = "brightness";
+ field public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
+ field public static final String PARAMETER_COLOR_TUNE = "color_tune";
+ field public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
+ field public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
+ field public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
+ field public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
+ field public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
+ field public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
+ field public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
+ field public static final String PARAMETER_COLOR_TUNER_RED_OFFSET = "color_tuner_red_offset";
+ field public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
field public static final String PARAMETER_CONTRAST = "contrast";
+ field public static final String PARAMETER_DECONTOUR = "decontour";
+ field public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
+ field public static final String PARAMETER_FILM_MODE = "film_mode";
+ field public static final String PARAMETER_FLESH_TONE = "flesh_tone";
+ field public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+ field public static final String PARAMETER_HUE = "hue";
+ field public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
+ field public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
field public static final String PARAMETER_SATURATION = "saturation";
field public static final String PARAMETER_SHARPNESS = "sharpness";
}
@@ -27207,13 +27279,14 @@ package android.media.quality {
}
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method public void addActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method public void createPictureProfile(@NonNull android.media.quality.PictureProfile);
method public void createSoundProfile(@NonNull android.media.quality.SoundProfile);
- method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles();
- method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles();
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles(boolean);
+ method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles(boolean);
method @NonNull public java.util.List<android.media.quality.ParamCapability> getParamCapabilities(@NonNull java.util.List<java.lang.String>);
- method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String);
- method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String);
+ method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String, boolean);
+ method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String, boolean);
method public boolean isAmbientBacklightEnabled();
method public boolean isAutoPictureQualityEnabled();
method public boolean isAutoSoundQualityEnabled();
@@ -27221,6 +27294,7 @@ package android.media.quality {
method public void registerAmbientBacklightCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.AmbientBacklightCallback);
method public void registerPictureProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.PictureProfileCallback);
method public void registerSoundProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.SoundProfileCallback);
+ method public void removeActiveProcessingPictureListener(@NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method public void removePictureProfile(@NonNull String);
method public void removeSoundProfile(@NonNull String);
method public void setAmbientBacklightEnabled(boolean);
@@ -27232,6 +27306,10 @@ package android.media.quality {
method public void updateSoundProfile(@NonNull String, @NonNull android.media.quality.SoundProfile);
}
+ public static interface MediaQualityManager.ActiveProcessingPictureListener {
+ method public void onActiveProcessingPicturesChanged(@NonNull java.util.List<android.media.quality.ActiveProcessingPicture>);
+ }
+
public abstract static class MediaQualityManager.AmbientBacklightCallback {
ctor public MediaQualityManager.AmbientBacklightCallback();
method public void onAmbientBacklightEvent(@NonNull android.media.quality.AmbientBacklightEvent);
@@ -27239,7 +27317,7 @@ package android.media.quality {
public abstract static class MediaQualityManager.PictureProfileCallback {
ctor public MediaQualityManager.PictureProfileCallback();
- method public void onError(int);
+ method public void onError(@Nullable String, int);
method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
method public void onPictureProfileAdded(@NonNull String, @NonNull android.media.quality.PictureProfile);
method public void onPictureProfileRemoved(@NonNull String, @NonNull android.media.quality.PictureProfile);
@@ -27248,7 +27326,7 @@ package android.media.quality {
public abstract static class MediaQualityManager.SoundProfileCallback {
ctor public MediaQualityManager.SoundProfileCallback();
- method public void onError(int);
+ method public void onError(@Nullable String, int);
method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
method public void onSoundProfileAdded(@NonNull String, @NonNull android.media.quality.SoundProfile);
method public void onSoundProfileRemoved(@NonNull String, @NonNull android.media.quality.SoundProfile);
@@ -29892,7 +29970,7 @@ package android.net.http {
public class X509TrustManagerExtensions {
ctor public X509TrustManagerExtensions(javax.net.ssl.X509TrustManager) throws java.lang.IllegalArgumentException;
method public java.util.List<java.security.cert.X509Certificate> checkServerTrusted(java.security.cert.X509Certificate[], String, String) throws java.security.cert.CertificateException;
- method @FlaggedApi("android.net.platform.flags.x509_extensions_certificate_transparency") @NonNull public java.util.List<java.security.cert.X509Certificate> checkServerTrusted(@NonNull java.security.cert.X509Certificate[], @Nullable byte[], @Nullable byte[], @NonNull String, @NonNull String) throws java.security.cert.CertificateException;
+ method @FlaggedApi("android.security.certificate_transparency_configuration") @NonNull public java.util.List<java.security.cert.X509Certificate> checkServerTrusted(@NonNull java.security.cert.X509Certificate[], @Nullable byte[], @Nullable byte[], @NonNull String, @NonNull String) throws java.security.cert.CertificateException;
method public boolean isSameTrustConfiguration(String, String);
method public boolean isUserAddedCertificate(java.security.cert.X509Certificate);
}
@@ -34701,7 +34779,10 @@ package android.os {
method public android.os.MessageQueue getMessageQueue();
method public boolean hasMessages(android.os.Handler, Object, int);
method public boolean hasMessages(android.os.Handler, Object, Runnable);
+ method @FlaggedApi("android.os.message_queue_testability") public boolean isBlockedOnSyncBarrier();
method public android.os.Message next();
+ method @FlaggedApi("android.os.message_queue_testability") @Nullable public Long peekWhen();
+ method @FlaggedApi("android.os.message_queue_testability") @Nullable public android.os.Message poll();
method public void recycle(android.os.Message);
method public void release();
}
@@ -40981,7 +41062,7 @@ package android.service.autofill {
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int UI_TYPE_CREDMAN = 4; // 0x4
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int UI_TYPE_CREDENTIAL_MANAGER = 4; // 0x4
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1
@@ -42550,9 +42631,10 @@ package android.service.settings.preferences {
method public boolean isWritable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR;
- field public static final int INTENT_ONLY = 2; // 0x2
- field public static final int NOT_SENSITIVE = 0; // 0x0
- field public static final int SENSITIVE = 1; // 0x1
+ field public static final int EXPECT_POST_CONFIRMATION = 1; // 0x1
+ field public static final int EXPECT_PRE_CONFIRMATION = 2; // 0x2
+ field public static final int NO_DIRECT_ACCESS = 3; // 0x3
+ field public static final int NO_SENSITIVITY = 0; // 0x0
}
public static final class SettingsPreferenceMetadata.Builder {
@@ -44820,11 +44902,11 @@ package android.telephony {
field public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = "carrier_vvm_package_name_string_array";
field public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
field public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
- field public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
- field public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
- field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
- field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
- field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
field public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
@@ -44851,7 +44933,7 @@ package android.telephony {
field public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING = "default_vm_number_roaming_and_ims_unregistered_string";
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
- field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = "disable_dun_apn_while_roaming_with_preset_apn_bool";
field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
@@ -44972,6 +45054,7 @@ package android.telephony {
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array";
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE = "regional_satellite_earfcn_bundle";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
@@ -44985,10 +45068,12 @@ package android.telephony {
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = "satellite_data_support_mode_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DISPLAY_NAME_STRING = "satellite_display_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING = "satellite_entitlement_app_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final String KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL = "satellite_ignore_data_roaming_setting_bool";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING = "satellite_information_redirect_url_string";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
@@ -44996,12 +45081,14 @@ package android.telephony {
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = "satellite_roaming_turn_off_session_for_emergency_call_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT = "satellite_sos_max_datagram_size_bytes_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY = "satellite_supported_msg_apps_string_array";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
- field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool";
field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
- field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
@@ -45031,7 +45118,7 @@ package android.telephony {
field public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL = "support_enhanced_call_blocking_bool";
field public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = "support_ims_conference_event_package_bool";
field public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
- field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array";
field public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool";
@@ -45041,7 +45128,7 @@ package android.telephony {
field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL = "use_ip_for_calling_indicator_bool";
- field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
@@ -45477,13 +45564,13 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
}
- public final class CellIdentityCdma extends android.telephony.CellIdentity {
- method public int getBasestationId();
- method public int getLatitude();
- method public int getLongitude();
- method public int getNetworkId();
- method public int getSystemId();
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellIdentityCdma extends android.telephony.CellIdentity {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getBasestationId();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getLatitude();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getLongitude();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getNetworkId();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getSystemId();
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
@@ -45575,11 +45662,11 @@ package android.telephony {
field public static final long UNAVAILABLE_LONG = 9223372036854775807L; // 0x7fffffffffffffffL
}
- public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
- method @NonNull public android.telephony.CellIdentityCdma getCellIdentity();
- method @NonNull public android.telephony.CellSignalStrengthCdma getCellSignalStrength();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoCdma> CREATOR;
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellIdentityCdma getCellIdentity();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellSignalStrengthCdma getCellSignalStrength();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoCdma> CREATOR;
}
public final class CellInfoGsm extends android.telephony.CellInfo implements android.os.Parcelable {
@@ -47158,11 +47245,11 @@ package android.telephony {
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public String getManualNetworkSelectionPlmn();
- method @Nullable public String getManufacturerCode();
- method @Nullable public String getManufacturerCode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @Nullable public String getManufacturerCode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @Nullable public String getManufacturerCode(int);
method public static long getMaximumCallComposerPictureSize();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
method public String getMmsUAProfUrl();
method public String getMmsUserAgent();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
@@ -47308,10 +47395,10 @@ package android.telephony {
field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2; // 0x2
field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3; // 0x3
field public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0; // 0x0
- field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
- field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
- field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
- field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
@@ -47331,9 +47418,9 @@ package android.telephony {
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
field public static final int DEFAULT_PORT_INDEX = 0; // 0x0
- field public static final int ERI_FLASH = 2; // 0x2
- field public static final int ERI_OFF = 1; // 0x1
- field public static final int ERI_ON = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_FLASH = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_OFF = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_ON = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final String EVENT_DISPLAY_EMERGENCY_MESSAGE = "android.telephony.event.DISPLAY_EMERGENCY_MESSAGE";
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL";
@@ -47374,11 +47461,11 @@ package android.telephony {
field public static final int NETWORK_SELECTION_MODE_AUTO = 1; // 0x1
field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2
field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0
- field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
- field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
- field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
@@ -47395,12 +47482,12 @@ package android.telephony {
field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
- field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
- field public static final int NETWORK_TYPE_EHRPD = 14; // 0xe
- field public static final int NETWORK_TYPE_EVDO_0 = 5; // 0x5
- field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
- field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EHRPD = 14; // 0xe
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_0 = 5; // 0x5
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
field public static final int NETWORK_TYPE_GSM = 16; // 0x10
field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
@@ -47414,7 +47501,7 @@ package android.telephony {
field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
- field public static final int PHONE_TYPE_CDMA = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int PHONE_TYPE_CDMA = 2; // 0x2
field public static final int PHONE_TYPE_GSM = 1; // 0x1
field public static final int PHONE_TYPE_NONE = 0; // 0x0
field public static final int PHONE_TYPE_SIP = 3; // 0x3
@@ -49282,7 +49369,7 @@ package android.text {
method public static void dumpSpans(CharSequence, android.util.Printer, String);
method public static CharSequence ellipsize(CharSequence, android.text.TextPaint, float, android.text.TextUtils.TruncateAt);
method public static CharSequence ellipsize(CharSequence, android.text.TextPaint, float, android.text.TextUtils.TruncateAt, boolean, @Nullable android.text.TextUtils.EllipsizeCallback);
- method public static boolean equals(CharSequence, CharSequence);
+ method public static boolean equals(@Nullable CharSequence, @Nullable CharSequence);
method public static CharSequence expandTemplate(CharSequence, java.lang.CharSequence...);
method public static int getCapsMode(CharSequence, int, int);
method public static void getChars(CharSequence, int, int, char[], int);
@@ -55178,8 +55265,8 @@ package android.view {
method public abstract void setTransformation(android.graphics.Matrix);
method public abstract void setVisibility(int);
method public abstract void setWebDomain(@Nullable String);
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE = "android.view.extra.VIRTUAL_STRUCTURE_TYPE";
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER = "android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
}
public abstract static class ViewStructure.HtmlInfo {
@@ -58292,6 +58379,7 @@ package android.view.textclassifier {
method @NonNull @WorkerThread public default android.view.textclassifier.TextSelection suggestSelection(@NonNull android.view.textclassifier.TextSelection.Request);
method @NonNull @WorkerThread public default android.view.textclassifier.TextSelection suggestSelection(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @Nullable android.os.LocaleList);
field public static final String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String EXTRA_TEXT_ORIGIN_PACKAGE = "android.view.textclassifier.extra.TEXT_ORIGIN_PACKAGE";
field public static final String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
field public static final String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
field public static final android.view.textclassifier.TextClassifier NO_OP;
@@ -58301,6 +58389,7 @@ package android.view.textclassifier {
field public static final String TYPE_EMAIL = "email";
field public static final String TYPE_FLIGHT_NUMBER = "flight";
field public static final String TYPE_OTHER = "other";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String TYPE_OTP = "otp";
field public static final String TYPE_PHONE = "phone";
field public static final String TYPE_UNKNOWN = "";
field public static final String TYPE_URL = "url";
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index ad5bd31828e0..e71dffaf152d 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,10 +1,4 @@
// Baseline format: 1.0
-ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_TYPE:
- Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
-ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
- Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
-
-
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
@@ -1191,10 +1185,6 @@ UnflaggedApi: android.R.dimen#system_corner_radius_xlarge:
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xlarge
UnflaggedApi: android.R.dimen#system_corner_radius_xsmall:
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xsmall
-UnflaggedApi: android.R.integer#status_bar_notification_info_maxnum:
- Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.integer.status_bar_notification_info_maxnum
-UnflaggedApi: android.R.string#status_bar_notification_info_overflow:
- Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.string.status_bar_notification_info_overflow
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
New API must be flagged with @FlaggedApi: field android.accessibilityservice.AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bca2ce2473c5..40069aa00106 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -130,7 +130,6 @@ package android.content.pm {
public abstract class PackageManager {
method @NonNull public String getSdkSandboxPackageName();
- method @FlaggedApi("android.content.pm.cloud_compilation_pm") @NonNull public static android.content.pm.SigningInfo getVerifiedSigningInfo(@NonNull String, int) throws android.content.pm.SigningInfoException;
method @RequiresPermission(android.Manifest.permission.MAKE_UID_VISIBLE) public void makeUidVisible(int, int);
field public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.VERIFICATION_ROOT_HASH";
field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
@@ -141,18 +140,6 @@ package android.content.pm {
method @NonNull public String getPackageName();
}
- public final class SigningInfo implements android.os.Parcelable {
- method @FlaggedApi("android.content.pm.cloud_compilation_pm") public boolean signersMatchExactly(@NonNull android.content.pm.SigningInfo);
- field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_JAR = 1; // 0x1
- field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V2 = 2; // 0x2
- field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V3 = 3; // 0x3
- field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V4 = 4; // 0x4
- }
-
- @FlaggedApi("android.content.pm.cloud_compilation_pm") public class SigningInfoException extends java.lang.Exception {
- method @FlaggedApi("android.content.pm.cloud_compilation_pm") public int getCode();
- }
-
}
package android.hardware.usb {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a5939700e5aa..f0f0fc98881e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10,6 +10,7 @@ package android {
field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH";
field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
+ field @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public static final String ACCESS_FINE_POWER_MONITORS = "android.permission.ACCESS_FINE_POWER_MONITORS";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
field @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") public static final String ACCESS_HIDDEN_PROFILES_FULL = "android.permission.ACCESS_HIDDEN_PROFILES_FULL";
@@ -26,6 +27,7 @@ package android {
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field @FlaggedApi("android.app.smartspace.flags.access_smartspace") public static final String ACCESS_SMARTSPACE = "android.permission.ACCESS_SMARTSPACE";
field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String ACCESS_TEXT_CLASSIFIER_BY_TYPE = "android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE";
field public static final String ACCESS_TUNED_INFO = "android.permission.ACCESS_TUNED_INFO";
field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
field public static final String ACCESS_TV_SHARED_FILTER = "android.permission.ACCESS_TV_SHARED_FILTER";
@@ -148,7 +150,6 @@ package android {
field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE";
field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
- field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS";
field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS_TRUSTED = "android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED";
field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
@@ -328,6 +329,7 @@ package android {
field public static final String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final String READ_SAFETY_CENTER_STATUS = "android.permission.READ_SAFETY_CENTER_STATUS";
field public static final String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final String READ_SUBSCRIPTION_PLANS = "android.permission.READ_SUBSCRIPTION_PLANS";
field @FlaggedApi("android.app.system_terms_of_address_enabled") public static final String READ_SYSTEM_GRAMMATICAL_GENDER = "android.permission.READ_SYSTEM_GRAMMATICAL_GENDER";
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
@@ -517,7 +519,9 @@ package android {
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultNotes = 17039429; // 0x1040045
- field @FlaggedApi("android.permission.flags.cross_user_role_platform_api_enabled") public static final int config_defaultReservedForTestingProfileGroupExclusivity;
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceDeviceConfigNamespace;
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceService;
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceSandboxedInferenceService;
field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048
field public static final int config_defaultSms = 17039396; // 0x1040024
field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049
@@ -550,6 +554,7 @@ package android {
field public static final int config_systemTextIntelligence = 17039414; // 0x1040036
field public static final int config_systemUi = 17039418; // 0x104003a
field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
+ field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence;
field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
field public static final int config_systemWearHealthService = 17039428; // 0x1040044
field public static final int config_systemWellbeing = 17039408; // 0x1040030
@@ -2275,149 +2280,6 @@ package android.app.job {
}
-package android.app.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface DownloadCallback {
- method public void onDownloadCompleted(@NonNull android.os.PersistableBundle);
- method public void onDownloadFailed(int, @Nullable String, @NonNull android.os.PersistableBundle);
- method public default void onDownloadProgress(long);
- method public default void onDownloadStarted(long);
- field public static final int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; // 0x3
- field public static final int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; // 0x2
- field public static final int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; // 0x1
- field public static final int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; // 0x4
- field public static final int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; // 0x0
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Feature implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.os.PersistableBundle getFeatureParams();
- method public int getId();
- method @Nullable public String getModelName();
- method @Nullable public String getName();
- method public int getType();
- method public int getVariant();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Feature> CREATOR;
- }
-
- public static final class Feature.Builder {
- ctor public Feature.Builder(int);
- method @NonNull public android.app.ondeviceintelligence.Feature build();
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setFeatureParams(@NonNull android.os.PersistableBundle);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setModelName(@NonNull String);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setName(@NonNull String);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setType(int);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setVariant(int);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FeatureDetails implements android.os.Parcelable {
- ctor public FeatureDetails(int, @NonNull android.os.PersistableBundle);
- ctor public FeatureDetails(int);
- method public int describeContents();
- method @NonNull public android.os.PersistableBundle getFeatureDetailParams();
- method public int getFeatureStatus();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FeatureDetails> CREATOR;
- field public static final int FEATURE_STATUS_AVAILABLE = 3; // 0x3
- field public static final int FEATURE_STATUS_DOWNLOADABLE = 1; // 0x1
- field public static final int FEATURE_STATUS_DOWNLOADING = 2; // 0x2
- field public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; // 0x4
- field public static final int FEATURE_STATUS_UNAVAILABLE = 0; // 0x0
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public final class InferenceInfo implements android.os.Parcelable {
- method public int describeContents();
- method public long getEndTimeMillis();
- method public long getStartTimeMillis();
- method public long getSuspendedTimeMillis();
- method public int getUid();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.InferenceInfo> CREATOR;
- }
-
- public static final class InferenceInfo.Builder {
- ctor public InferenceInfo.Builder(int);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo build();
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setEndTimeMillis(long);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setStartTimeMillis(long);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setSuspendedTimeMillis(long);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public class OnDeviceIntelligenceException extends java.lang.Exception {
- ctor public OnDeviceIntelligenceException(int, @NonNull String, @NonNull android.os.PersistableBundle);
- ctor public OnDeviceIntelligenceException(int, @NonNull android.os.PersistableBundle);
- ctor public OnDeviceIntelligenceException(int, @NonNull String);
- ctor public OnDeviceIntelligenceException(int);
- method public int getErrorCode();
- method @NonNull public android.os.PersistableBundle getErrorParams();
- field public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 100; // 0x64
- field public static final int PROCESSING_ERROR_BAD_DATA = 2; // 0x2
- field public static final int PROCESSING_ERROR_BAD_REQUEST = 3; // 0x3
- field public static final int PROCESSING_ERROR_BUSY = 9; // 0x9
- field public static final int PROCESSING_ERROR_CANCELLED = 7; // 0x7
- field public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; // 0x5
- field public static final int PROCESSING_ERROR_INTERNAL = 14; // 0xe
- field public static final int PROCESSING_ERROR_IPC_ERROR = 6; // 0x6
- field public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; // 0x8
- field public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; // 0x4
- field public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; // 0xc
- field public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; // 0xb
- field public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; // 0xa
- field public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; // 0xf
- field public static final int PROCESSING_ERROR_SUSPENDED = 13; // 0xd
- field public static final int PROCESSING_ERROR_UNKNOWN = 1; // 0x1
- field public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 200; // 0xc8
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class OnDeviceIntelligenceManager {
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeature(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ondeviceintelligence.InferenceInfo> getLatestInferenceInfo(long);
- method @Nullable @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public String getRemoteServicePackageName();
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getVersion(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.LongConsumer);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void listFeatures(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.ProcessingCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingProcessingCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenInfo(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.TokenInfo,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2
- field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0
- field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface ProcessingCallback {
- method public default void onDataAugmentRequest(@NonNull android.os.Bundle, @NonNull java.util.function.Consumer<android.os.Bundle>);
- method public void onError(@NonNull android.app.ondeviceintelligence.OnDeviceIntelligenceException);
- method public void onResult(@NonNull android.os.Bundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class ProcessingSignal {
- ctor public ProcessingSignal();
- method public void sendSignal(@NonNull android.os.PersistableBundle);
- method public void setOnProcessingSignalCallback(@NonNull java.util.concurrent.Executor, @Nullable android.app.ondeviceintelligence.ProcessingSignal.OnProcessingSignalCallback);
- }
-
- public static interface ProcessingSignal.OnProcessingSignalCallback {
- method public void onSignalReceived(@NonNull android.os.PersistableBundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface StreamingProcessingCallback extends android.app.ondeviceintelligence.ProcessingCallback {
- method public void onPartialResult(@NonNull android.os.Bundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class TokenInfo implements android.os.Parcelable {
- ctor public TokenInfo(long, @NonNull android.os.PersistableBundle);
- ctor public TokenInfo(long);
- method public int describeContents();
- method public long getCount();
- method @NonNull public android.os.PersistableBundle getInfoParams();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.TokenInfo> CREATOR;
- }
-
-}
-
package android.app.people {
public final class PeopleManager {
@@ -5227,6 +5089,7 @@ package android.hardware.contexthub {
field public static final int REASON_ENDPOINT_STOPPED = 6; // 0x6
field public static final int REASON_FAILURE = 0; // 0x0
field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3
+ field public static final int REASON_PERMISSION_DENIED = 9; // 0x9
}
public static final class HubEndpoint.Builder {
@@ -5265,7 +5128,7 @@ package android.hardware.contexthub {
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
- method @Nullable public android.hardware.contexthub.HubServiceInfo getServiceInfo();
+ method @Nullable public String getServiceDescriptor();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
}
@@ -5318,7 +5181,7 @@ package android.hardware.contexthub {
@FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
- method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo);
+ method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
}
@@ -6324,7 +6187,7 @@ package android.hardware.location {
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo, @NonNull android.hardware.contexthub.HubServiceInfo);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
@@ -8128,12 +7991,13 @@ package android.media.musicrecognition {
package android.media.quality {
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method public void addGlobalActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method @NonNull public java.util.List<java.lang.String> getPictureProfileAllowList();
method @NonNull public java.util.List<java.lang.String> getPictureProfilePackageNames();
- method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String);
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String, boolean);
method @NonNull public java.util.List<java.lang.String> getSoundProfileAllowList();
method @NonNull public java.util.List<java.lang.String> getSoundProfilePackageNames();
- method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String);
+ method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String, boolean);
method public void setAutoPictureQualityEnabled(boolean);
method public void setAutoSoundQualityEnabled(boolean);
method public boolean setDefaultPictureProfile(@Nullable String);
@@ -11111,8 +10975,8 @@ package android.nfc.cardemulation {
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
method @FlaggedApi("android.nfc.nfc_observe_mode") public void setShouldDefaultToObserveMode(boolean);
- method @FlaggedApi("android.nfc.nfc_associated_role_services") public boolean shareRolePriority();
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean shouldDefaultToObserveMode();
+ method @FlaggedApi("android.nfc.nfc_associated_role_services") public boolean wantsRoleHolderPriority();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
field @FlaggedApi("android.permission.flags.wallet_role_icon_property_enabled") public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL = "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL";
@@ -12615,6 +12479,7 @@ package android.provider {
field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
field public static final String ACTION_SHOW_RESTRICTED_SETTING_DIALOG = "android.settings.SHOW_RESTRICTED_SETTING_DIALOG";
+ field @FlaggedApi("com.android.internal.telephony.flags.action_sim_preference_settings") public static final String ACTION_SIM_PREFERENCE_SETTINGS = "android.settings.SIM_PREFERENCE_SETTINGS";
field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
field public static final String ACTION_TETHER_UNSUPPORTED_CARRIER_UI = "android.settings.TETHER_UNSUPPORTED_CARRIER_UI";
@@ -12850,14 +12715,14 @@ package android.security.authenticationpolicy {
}
@FlaggedApi("android.security.secure_lockdown") public final class DisableSecureLockDeviceParams implements android.os.Parcelable {
- ctor public DisableSecureLockDeviceParams(@NonNull String);
+ ctor public DisableSecureLockDeviceParams(@NonNull CharSequence);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.DisableSecureLockDeviceParams> CREATOR;
}
@FlaggedApi("android.security.secure_lockdown") public final class EnableSecureLockDeviceParams implements android.os.Parcelable {
- ctor public EnableSecureLockDeviceParams(@NonNull String);
+ ctor public EnableSecureLockDeviceParams(@NonNull CharSequence);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.EnableSecureLockDeviceParams> CREATOR;
@@ -13789,39 +13654,6 @@ package android.service.oemlock {
}
-package android.service.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
- ctor public OnDeviceIntelligenceService();
- method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public abstract void onDownloadFeature(int, @NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback);
- method public abstract void onGetFeature(int, int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onGetFeatureDetails(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
- method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
- method public abstract void onInferenceServiceConnected();
- method public abstract void onInferenceServiceDisconnected();
- method public abstract void onListFeatures(int, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public final void updateProcessingState(@NonNull android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceSandboxedInferenceService extends android.app.Service {
- ctor public OnDeviceSandboxedInferenceService();
- method public final void fetchFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
- method @NonNull public java.util.concurrent.Executor getCallbackExecutor();
- method public final void getReadOnlyFileDescriptor(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.ParcelFileDescriptor>) throws java.io.FileNotFoundException;
- method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @NonNull public abstract void onProcessRequest(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.ProcessingCallback);
- method @NonNull public abstract void onProcessRequestStreaming(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingProcessingCallback);
- method @NonNull public abstract void onTokenInfoRequest(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.TokenInfo,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onUpdateProcessingState(@NonNull android.os.Bundle, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
- field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
- }
-
-}
-
package android.service.persistentdata {
@FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
@@ -14802,7 +14634,7 @@ package android.telecom {
field public static final int CALLTYPE_INCOMING = 1; // 0x1
field public static final int CALLTYPE_OUTGOING = 2; // 0x2
field public static final int CALLTYPE_UNKNOWN = 0; // 0x0
- field public static final int CDMA_PHONE = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_PHONE = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics> CREATOR;
field public static final int GSM_PHONE = 2; // 0x2
field public static final int IMS_PHONE = 4; // 0x4
@@ -15183,7 +15015,7 @@ package android.telephony {
field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int";
field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int";
field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int";
- field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
public static final class CarrierConfigManager.Wifi {
@@ -15270,8 +15102,8 @@ package android.telephony {
ctor public CellBroadcastService();
method @NonNull @WorkerThread public abstract CharSequence getCellBroadcastAreaInfo(int);
method @CallSuper public android.os.IBinder onBind(@Nullable android.content.Intent);
- method public abstract void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
- method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public abstract void onGsmCellBroadcastSms(int, @NonNull byte[]);
field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
}
@@ -15281,9 +15113,9 @@ package android.telephony {
method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
}
- public final class CellIdentityCdma extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellIdentityCdma extends android.telephony.CellIdentity {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
@@ -15699,16 +15531,16 @@ package android.telephony {
field public static final int BUSY = 17; // 0x11
field public static final int CALL_BARRED = 240; // 0xf0
field public static final int CALL_REJECTED = 21; // 0x15
- field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
- field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
- field public static final int CDMA_DROP = 1001; // 0x3e9
- field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
- field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
- field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
- field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
- field public static final int CDMA_REORDER = 1003; // 0x3eb
- field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
- field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_DROP = 1001; // 0x3e9
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_REORDER = 1003; // 0x3eb
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SO_REJECT = 1004; // 0x3ec
field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
@@ -16242,14 +16074,14 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageName();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageNameForLogicalSlot(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
- method public String getCdmaPrlVersion();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaSubscriptionMode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public String getCdmaPrlVersion();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaSubscriptionMode();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public java.util.List<android.telephony.CellBroadcastIdRange> getCellBroadcastIdRanges();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
@@ -16339,7 +16171,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -16348,8 +16180,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingEnabled(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public void setCellBroadcastIdRanges(@NonNull java.util.List<android.telephony.CellBroadcastIdRange>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
@@ -16413,9 +16245,9 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
- field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
- field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
field public static final int CELL_BROADCAST_RESULT_FAIL_ACTIVATION = 3; // 0x3
field public static final int CELL_BROADCAST_RESULT_FAIL_CONFIG = 2; // 0x2
field public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; // 0x0
@@ -16632,21 +16464,21 @@ package android.telephony {
package android.telephony.cdma {
- public final class CdmaSmsCbProgramData implements android.os.Parcelable {
- method public int describeContents();
- method public int getCategory();
- method public int getOperation();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
- field public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
- field public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
- field public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
- field public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
- field public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
- field public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
- field public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
- field public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CdmaSmsCbProgramData implements android.os.Parcelable {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int describeContents();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getCategory();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getOperation();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
}
}
@@ -17974,7 +17806,7 @@ package android.telephony.ims {
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12; // 0xc
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9
@@ -18823,6 +18655,10 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteDisallowedReasonsCallback {
+ method public void onSatelliteDisallowedReasonsChanged(@NonNull int[]);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.Integer> getBands();
@@ -18838,6 +18674,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @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 @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int[] getSatelliteDisallowedReasons();
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -18848,6 +18685,8 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForSatelliteDisallowedReasonsChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDisallowedReasonsCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSelectedNbIotSatelliteSubscriptionChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSupportedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -18861,7 +18700,9 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAccessConfigurationForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteAccessConfiguration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteSubscriberProvisionStatus(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSelectedNbIotSatelliteSubscriptionId(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Integer,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
@@ -18873,6 +18714,8 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDisallowedReasonsChanged(@NonNull android.telephony.satellite.SatelliteDisallowedReasonsCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSelectedNbIotSatelliteSubscriptionChanged(@NonNull android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSupportedStateChanged(@NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION = "android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = "android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
@@ -18944,6 +18787,7 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_REACHABLE = 18; // 0x12
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20; // 0x14
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NO_RESOURCES = 12; // 0xc
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION = 30; // 0x1e
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10; // 0xa
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15; // 0xf
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_FAILED = 9; // 0x9
@@ -19046,6 +18890,10 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramStateChanged(int, int, int, int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SelectedNbIotSatelliteSubscriptionCallback {
+ method public void onSelectedNbIotSatelliteSubscriptionChanged(int);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SystemSelectionSpecifier implements android.os.Parcelable {
method public int describeContents();
method @NonNull public int[] getBands();
@@ -19291,6 +19139,20 @@ package android.view.inputmethod {
}
+package android.view.textclassifier {
+
+ public final class TextClassificationManager {
+ method @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE) public android.view.textclassifier.TextClassifier getClassifier(int);
+ }
+
+ public interface TextClassifier {
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_ANDROID_DEFAULT = 2; // 0x2
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_DEVICE_DEFAULT = 1; // 0x1
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_SELF_PROVIDED = 0; // 0x0
+ }
+
+}
+
package android.view.translation {
public final class TranslationCapability implements android.os.Parcelable {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 7c43891f13f2..3b9ef959e797 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -505,6 +505,8 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
FlaggedApiLiteral: android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES).
+FlaggedApiLiteral: android.Manifest.permission#ACCESS_TEXT_CLASSIFIER_BY_TYPE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED).
FlaggedApiLiteral: android.Manifest.permission#BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED).
FlaggedApiLiteral: android.Manifest.permission#BIND_VERIFICATION_AGENT:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5442ab7d1d9e..5171e68cdb85 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -657,6 +657,7 @@ package android.app.admin {
field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e
field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int OPERATION_SET_APP_FUNCTIONS_POLICY = 42; // 0x2a
field public static final int OPERATION_SET_CAMERA_DISABLED = 31; // 0x1f
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41; // 0x29
field public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32; // 0x20
@@ -1401,9 +1402,9 @@ package android.credentials.selection {
method @NonNull public android.credentials.selection.GetCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.selection.Entry);
}
- @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory {
- method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.content.Context, @NonNull android.os.IBinder, boolean, @NonNull String);
- method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.content.Context, @NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver);
+ @FlaggedApi("android.credentials.flags.propagate_user_context_for_intent_creation") public class IntentFactory {
+ method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.content.Context, @NonNull android.os.IBinder, boolean, @NonNull String, int);
+ method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.content.Context, @NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver, int);
}
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public abstract class ProviderData implements android.os.Parcelable {
@@ -2430,6 +2431,12 @@ package android.os {
method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException;
}
+ public class Handler {
+ method @FlaggedApi("android.os.mainline_vcn_platform_api") public final boolean hasMessagesOrCallbacks();
+ method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeCallbacksAndEqualMessages(@Nullable Object);
+ method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeEqualMessages(int, @Nullable Object);
+ }
+
public class IpcDataCache<Query, Result> extends android.app.PropertyInvalidatedCache<Query,Result> {
ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
method public static void disableForCurrentProcess(@NonNull String);
@@ -3246,14 +3253,6 @@ package android.service.notification {
}
-package android.service.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
- method public void onReady();
- }
-
-}
-
package android.service.quickaccesswallet {
public interface QuickAccessWalletClient extends java.io.Closeable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 9140bdf4fba7..349b4edffc32 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,10 +1,4 @@
// Baseline format: 1.0
-ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_TYPE:
- Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
-ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
- Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
-
-
BannedThrow: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
Methods must not throw unchecked exceptions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 38aea64386a0..fee8cdb1ce51 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1279,7 +1279,24 @@ public class Activity extends ContextThemeWrapper
*
* <p>This method should be utilized when an activity wants to nudge the user to switch
* to the web application in cases where the web may provide the user with a better
- * experience. Note that this method does not guarantee that the education will be shown.</p>
+ * experience. Note that this method does not guarantee that the education will be shown.
+ *
+ * <p>The number of times that the "Open in browser" education can be triggered by this method
+ * is limited per application, and, when shown, the education appears above the app's content.
+ * For these reasons, developers should use this method sparingly when it is least
+ * disruptive to the user to show the education and when it is optimal to switch the user to a
+ * browser session. Before requesting to show the education, developers should assert that they
+ * have set a link that can be used by the "Open in browser" feature through either
+ * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+ * {@link AssistContent#setWebUri} so that users are navigated to a relevant page if they choose
+ * to switch to the browser. If a URI is not set using either method, "Open in browser" will
+ * utilize a generic link if available which will direct users to the homepage of the site
+ * associated with the app. The generic link is provided for a limited number of applications by
+ * the system and cannot be edited by developers. If none of these options contains a valid URI,
+ * the user will not be provided with the option to switch to the browser and the education will
+ * not be shown if requested.
+ *
+ * @see android.app.assist.AssistContent#EXTRA_SESSION_TRANSFER_WEB_URI
*/
@FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
public final void requestOpenInBrowserEducation() {
@@ -5765,8 +5782,7 @@ public class Activity extends ContextThemeWrapper
}
if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
- final int permissionCount = permissions.length;
- for (int i = 0; i < permissionCount; i++) {
+ for (int i = 0; i < permissions.length; i++) {
if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
throw new IllegalArgumentException("Cannot request renounced permission: "
+ permissions[i]);
@@ -5774,13 +5790,55 @@ public class Activity extends ContextThemeWrapper
}
}
- PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
- : createDeviceContext(deviceId).getPackageManager();
+ final Context context = getDeviceId() == deviceId ? this : createDeviceContext(deviceId);
+ if (Flags.permissionRequestShortCircuitEnabled()) {
+ int[] permissionsState = getPermissionRequestStates(context, permissions);
+ boolean hasRequestablePermission = false;
+ for (int i = 0; i < permissionsState.length; i++) {
+ if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_REQUESTABLE) {
+ hasRequestablePermission = true;
+ break;
+ }
+ }
+ // If none of the permissions is requestable, finish the request here.
+ if (!hasRequestablePermission) {
+ mHasCurrentPermissionsRequest = true;
+ Log.v(TAG, "No requestable permission in the request.");
+ int[] results = new int[permissionsState.length];
+ for (int i = 0; i < permissionsState.length; i++) {
+ if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_GRANTED) {
+ results[i] = PackageManager.PERMISSION_GRANTED;
+ } else {
+ results[i] = PackageManager.PERMISSION_DENIED;
+ }
+ }
+ // Currently permission request result is passed to the client app asynchronously
+ // in onRequestPermissionsResult, lets keep async behavior here as well.
+ mHandler.post(() -> {
+ mHasCurrentPermissionsRequest = false;
+ onRequestPermissionsResult(requestCode, permissions, results, deviceId);
+ });
+ return;
+ }
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
+ @NonNull
+ private int[] getPermissionRequestStates(@NonNull Context deviceContext,
+ @NonNull String[] permissions) {
+ final int size = permissions.length;
+ int[] results = new int[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ }
+ return results;
+ }
+
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {@link #requestPermissions}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 33ba05865042..69d3e8d4c0d2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1189,6 +1189,18 @@ public class ActivityManager {
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
}
+ /** @hide Should this process state be considered jank perceptible? */
+ public static final boolean isProcStateJankPerceptible(int procState) {
+ if (Flags.jankPerceptibleNarrow()) {
+ return procState == PROCESS_STATE_PERSISTENT_UI
+ || procState == PROCESS_STATE_TOP
+ || procState == PROCESS_STATE_IMPORTANT_FOREGROUND
+ || procState == PROCESS_STATE_TOP_SLEEPING;
+ } else {
+ return !isProcStateCached(procState);
+ }
+ }
+
/** @hide requestType for assist context: only basic information. */
public static final int ASSIST_CONTEXT_BASIC = 0;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index eccb6ffb281c..abdfb53537f8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -44,6 +44,8 @@ import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.util.ArraySet;
import android.util.Pair;
@@ -1352,6 +1354,14 @@ public abstract class ActivityManagerInternal {
String reason, int exitInfoReason);
/**
+ * Queries the offset data for a given method on a process.
+ * @hide
+ */
+ public abstract void getExecutableMethodFileOffsets(@NonNull String processName,
+ int pid, int uid, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback);
+
+ /**
* Add a creator token for all embedded intents (stored as extra) of the given intent.
*
* @param intent The given intent
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5d0b355170ad..717a2acb4b4a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -165,6 +165,10 @@ import android.os.TelephonyServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.permission.IPermissionManager;
import android.provider.BlockedNumberContract;
import android.provider.CalendarContract;
@@ -1372,7 +1376,8 @@ public final class ActivityThread extends ClientTransactionHandler
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
updateCompatOverrideScale(compatInfo);
- CompatibilityInfo.applyOverrideScaleIfNeeded(config);
+ updateCompatOverrideDisplayRotation(compatInfo);
+ CompatibilityInfo.applyOverrideIfNeeded(config);
sendMessage(H.BIND_APPLICATION, data);
}
@@ -1386,6 +1391,15 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ private void updateCompatOverrideDisplayRotation(@NonNull CompatibilityInfo info) {
+ if (info.isOverrideDisplayRotationRequired()) {
+ CompatibilityInfo.setOverrideDisplayRotation(info.applicationDisplayRotation);
+ } else {
+ CompatibilityInfo.setOverrideDisplayRotation(
+ WindowConfiguration.ROTATION_UNDEFINED);
+ }
+ }
+
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = entryPoint;
@@ -1961,8 +1975,12 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) {
- PropertyInvalidatedCache.dumpCacheInfo(pfd, args);
- IoUtils.closeQuietly(pfd);
+ try {
+ PropertyInvalidatedCache.dumpCacheInfo(pfd, args);
+ BroadcastStickyCache.dumpCacheInfo(pfd);
+ } finally {
+ IoUtils.closeQuietly(pfd);
+ }
}
private File getDatabasesDir(Context context) {
@@ -2036,6 +2054,7 @@ public final class ActivityThread extends ClientTransactionHandler
ucd.pkg = pkg;
ucd.info = info;
updateCompatOverrideScale(info);
+ updateCompatOverrideDisplayRotation(info);
sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
}
@@ -2225,6 +2244,29 @@ public final class ActivityThread extends ClientTransactionHandler
args.arg6 = uiTranslationSpec;
sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args);
}
+
+ @Override
+ public void getExecutableMethodFileOffsets(
+ @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback resultCallback) {
+ Method method = MethodDescriptorParser.parseMethodDescriptor(
+ getClass().getClassLoader(), methodDescriptor);
+ VMDebug.ExecutableMethodFileOffsets location =
+ VMDebug.getExecutableMethodFileOffsets(method);
+ try {
+ if (location == null) {
+ resultCallback.onResult(null);
+ return;
+ }
+ ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
+ ret.containerPath = location.getContainerPath();
+ ret.containerOffset = location.getContainerOffset();
+ ret.methodOffset = location.getMethodOffset();
+ resultCallback.onResult(ret);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@@ -3907,12 +3949,7 @@ public final class ActivityThread extends ClientTransactionHandler
if (mLastProcessState == processState) {
return;
}
- // Do not issue a transitional GC if we are transitioning between 2 cached states.
- // Only update if the state flips between cached and uncached or vice versa
- if (ActivityManager.isProcStateCached(mLastProcessState)
- != ActivityManager.isProcStateCached(processState)) {
- updateVmProcessState(processState);
- }
+ updateVmProcessState(mLastProcessState, processState);
mLastProcessState = processState;
if (localLOGV) {
Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
@@ -3921,18 +3958,21 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ /** Converts a process state to a VM process state. */
+ private static int toVmProcessState(int processState) {
+ final int state = ActivityManager.isProcStateJankPerceptible(processState)
+ ? VM_PROCESS_STATE_JANK_PERCEPTIBLE
+ : VM_PROCESS_STATE_JANK_IMPERCEPTIBLE;
+ return state;
+ }
+
/** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */
- // Currently ART VM only uses state updates for Transitional GC, and thus
- // this function initiates a Transitional GC for transitions into Cached apps states.
- private void updateVmProcessState(int processState) {
- // Only a transition into Cached state should result in a Transitional GC request
- // to the ART runtime. Update VM state to JANK_IMPERCEPTIBLE in that case.
- // Note that there are 4 possible cached states currently, all of which are
- // JANK_IMPERCEPTIBLE from GC point of view.
- final int state = ActivityManager.isProcStateCached(processState)
- ? VM_PROCESS_STATE_JANK_IMPERCEPTIBLE
- : VM_PROCESS_STATE_JANK_PERCEPTIBLE;
- VMRuntime.getRuntime().updateProcessState(state);
+ private void updateVmProcessState(int lastProcessState, int newProcessState) {
+ final int state = toVmProcessState(newProcessState);
+ if (lastProcessState == PROCESS_STATE_UNKNOWN
+ || state != toVmProcessState(lastProcessState)) {
+ VMRuntime.getRuntime().updateProcessState(state);
+ }
}
@Override
@@ -7058,12 +7098,9 @@ public final class ActivityThread extends ClientTransactionHandler
System.runFinalization();
System.gc();
}
- if (dhd.dumpBitmaps != null) {
- Bitmap.dumpAll(dhd.dumpBitmaps);
- }
try (ParcelFileDescriptor fd = dhd.fd) {
if (dhd.managed) {
- Debug.dumpHprofData(dhd.path, fd.getFileDescriptor());
+ Debug.dumpHprofData(dhd.path, fd.getFileDescriptor(), dhd.dumpBitmaps);
} else if (dhd.mallocInfo) {
Debug.dumpNativeMallocInfo(fd.getFileDescriptor());
} else {
@@ -7088,9 +7125,6 @@ public final class ActivityThread extends ClientTransactionHandler
if (dhd.finishCallback != null) {
dhd.finishCallback.sendResult(null);
}
- if (dhd.dumpBitmaps != null) {
- Bitmap.dumpAll(null); // clear dump
- }
}
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index 56ed290baf2e..b4dee2e937cb 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -19,7 +19,6 @@ package android.app;
parcelable AppOpsManager.PackageOps;
parcelable AppOpsManager.NoteOpEventProxyInfo;
parcelable AppOpsManager.NoteOpEvent;
-parcelable AppOpsManager.NotedOp;
parcelable AppOpsManager.OpFeatureEntry;
parcelable AppOpsManager.OpEntry;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c789e28cdbab..19138126698c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -262,24 +262,6 @@ public class AppOpsManager {
private static final Object sLock = new Object();
- // A map that records noted times for each op.
- private final ArrayMap<NotedOp, Integer> mPendingNotedOps = new ArrayMap<>();
- private final HandlerThread mHandlerThread;
- private final Handler mHandler;
- private static final int NOTE_OP_BATCHING_DELAY_MILLIS = 1000;
-
- private boolean isNoteOpBatchingSupported() {
- // If noteOp is called from system server no IPC is made, hence we don't need batching.
- if (Process.myUid() == Process.SYSTEM_UID) {
- return false;
- }
- return Flags.noteOpBatchingEnabled();
- }
-
- private final Object mBatchedNoteOpLock = new Object();
- @GuardedBy("mBatchedNoteOpLock")
- private boolean mIsBatchedNoteOpCallScheduled = false;
-
/** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
@GuardedBy("sLock")
private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
@@ -7484,135 +7466,6 @@ public class AppOpsManager {
}
/**
- * A NotedOp is an app op grouped in noteOp API and sent to the system server in a batch
- *
- * @hide
- */
- public static final class NotedOp implements Parcelable {
- private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
- private final @IntRange(from = 0) int mUid;
- private final @Nullable String mPackageName;
- private final @Nullable String mAttributionTag;
- private final int mVirtualDeviceId;
- private final @Nullable String mMessage;
- private final boolean mShouldCollectAsyncNotedOp;
- private final boolean mShouldCollectMessage;
-
- public NotedOp(int op, int uid, @Nullable String packageName,
- @Nullable String attributionTag, int virtualDeviceId, @Nullable String message,
- boolean shouldCollectAsyncNotedOp, boolean shouldCollectMessage) {
- mOp = op;
- mUid = uid;
- mPackageName = packageName;
- mAttributionTag = attributionTag;
- mVirtualDeviceId = virtualDeviceId;
- mMessage = message;
- mShouldCollectAsyncNotedOp = shouldCollectAsyncNotedOp;
- mShouldCollectMessage = shouldCollectMessage;
- }
-
- NotedOp(Parcel source) {
- mOp = source.readInt();
- mUid = source.readInt();
- mPackageName = source.readString();
- mAttributionTag = source.readString();
- mVirtualDeviceId = source.readInt();
- mMessage = source.readString();
- mShouldCollectAsyncNotedOp = source.readBoolean();
- mShouldCollectMessage = source.readBoolean();
- }
-
- public int getOp() {
- return mOp;
- }
-
- public int getUid() {
- return mUid;
- }
-
- public @Nullable String getPackageName() {
- return mPackageName;
- }
-
- public @Nullable String getAttributionTag() {
- return mAttributionTag;
- }
-
- public int getVirtualDeviceId() {
- return mVirtualDeviceId;
- }
-
- public @Nullable String getMessage() {
- return mMessage;
- }
-
- public boolean getShouldCollectAsyncNotedOp() {
- return mShouldCollectAsyncNotedOp;
- }
-
- public boolean getShouldCollectMessage() {
- return mShouldCollectMessage;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mOp);
- dest.writeInt(mUid);
- dest.writeString(mPackageName);
- dest.writeString(mAttributionTag);
- dest.writeInt(mVirtualDeviceId);
- dest.writeString(mMessage);
- dest.writeBoolean(mShouldCollectAsyncNotedOp);
- dest.writeBoolean(mShouldCollectMessage);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- NotedOp that = (NotedOp) o;
- return mOp == that.mOp && mUid == that.mUid && Objects.equals(mPackageName,
- that.mPackageName) && Objects.equals(mAttributionTag, that.mAttributionTag)
- && mVirtualDeviceId == that.mVirtualDeviceId && Objects.equals(mMessage,
- that.mMessage) && Objects.equals(mShouldCollectAsyncNotedOp,
- that.mShouldCollectAsyncNotedOp) && Objects.equals(mShouldCollectMessage,
- that.mShouldCollectMessage);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mOp, mUid, mPackageName, mAttributionTag, mVirtualDeviceId,
- mMessage, mShouldCollectAsyncNotedOp, mShouldCollectMessage);
- }
-
- @Override
- public String toString() {
- return "NotedOp{" + "mOp=" + mOp + ", mUid=" + mUid + ", mPackageName=" + mPackageName
- + ", mAttributionTag=" + mAttributionTag + ", mVirtualDeviceId="
- + mVirtualDeviceId + ", mMessage=" + mMessage + ", mShouldCollectAsyncNotedOp="
- + mShouldCollectAsyncNotedOp + ", mShouldCollectMessage="
- + mShouldCollectMessage + "}";
- }
-
-
- public static final @NonNull Creator<NotedOp> CREATOR =
- new Creator<>() {
- @Override public NotedOp createFromParcel(Parcel source) {
- return new NotedOp(source);
- }
-
- @Override public NotedOp[] newArray(int size) {
- return new NotedOp[size];
- }
- };
- }
-
- /**
* Computes the sum of the counts for the given flags in between the begin and
* end UID states.
*
@@ -8126,9 +7979,6 @@ public class AppOpsManager {
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
- mHandlerThread = new HandlerThread("AppOpsManager");
- mHandlerThread.start();
- mHandler = mHandlerThread.getThreadHandler();
if (mContext != null) {
final PackageManager pm = mContext.getPackageManager();
@@ -9465,74 +9315,15 @@ public class AppOpsManager {
}
}
- SyncNotedAppOp syncOp = null;
- boolean skipBinderCall = false;
- if (isNoteOpBatchingSupported()) {
- int mode = sAppOpModeCache.query(
- new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
- "noteOpNoThrow"));
- // For FOREGROUND mode, we still need to make a binder call to the system service
- // to translate it to ALLOWED or IGNORED. So no batching is needed.
- if (mode != MODE_FOREGROUND) {
- synchronized (mBatchedNoteOpLock) {
- NotedOp notedOp = new NotedOp(op, uid, packageName, attributionTag,
- virtualDeviceId, message, collectionMode == COLLECT_ASYNC,
- shouldCollectMessage);
-
- // Batch same noteOp calls and send them with their counters to the system
- // service asynchronously. The time window for batching is specified in
- // NOTE_OP_BATCHING_DELAY_MILLIS. Always allow the first noteOp call to go
- // through binder API. Accumulate subsequent same noteOp calls during the
- // time window in mPendingNotedOps.
- if (!mPendingNotedOps.containsKey(notedOp)) {
- mPendingNotedOps.put(notedOp, 0);
- } else {
- skipBinderCall = true;
- mPendingNotedOps.merge(notedOp, 1, Integer::sum);
- }
-
- if (!mIsBatchedNoteOpCallScheduled) {
- mHandler.postDelayed(() -> {
- ArrayMap<NotedOp, Integer> pendingNotedOpsCopy;
- synchronized(mBatchedNoteOpLock) {
- mIsBatchedNoteOpCallScheduled = false;
- pendingNotedOpsCopy =
- new ArrayMap<NotedOp, Integer>(mPendingNotedOps);
- mPendingNotedOps.clear();
- }
- for (int i = pendingNotedOpsCopy.size() - 1; i >= 0; i--) {
- if (pendingNotedOpsCopy.valueAt(i) == 0) {
- pendingNotedOpsCopy.removeAt(i);
- }
- }
- if (!pendingNotedOpsCopy.isEmpty()) {
- try {
- mService.noteOperationsInBatch(pendingNotedOpsCopy);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }, NOTE_OP_BATCHING_DELAY_MILLIS);
-
- mIsBatchedNoteOpCallScheduled = true;
- }
- }
-
- syncOp = new SyncNotedAppOp(mode, op, attributionTag, packageName);
- }
- }
-
- if (!skipBinderCall) {
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- } else {
- syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
- virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
- }
+ SyncNotedAppOp syncOp;
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ } else {
+ syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
}
-
if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 8b7ea0f8b46a..b21defbcc0e3 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -29,7 +29,7 @@ import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
@@ -86,9 +86,9 @@ public abstract class AppOpsManagerInternal {
*/
SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, Integer, SyncNotedAppOp> superImpl);
+ @Nullable String message, boolean shouldCollectMessage,
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl);
/**
* Allows overriding note proxy operation behavior.
diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java
index fe2e10752355..0ffbf2c3a6af 100644
--- a/core/java/android/app/BroadcastStickyCache.java
+++ b/core/java/android/app/BroadcastStickyCache.java
@@ -30,13 +30,20 @@ import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.IpcDataCache;
import android.os.IpcDataCache.Config;
+import android.os.ParcelFileDescriptor;
import android.os.UpdateLock;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.view.WindowManagerPolicyConstants;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
/** @hide */
public class BroadcastStickyCache {
@@ -71,8 +78,10 @@ public class BroadcastStickyCache {
@VisibleForTesting
public static final ArrayMap<String, String> sActionApiNameMap = new ArrayMap<>();
+ @GuardedBy("BroadcastStickyCache.class")
private static final ArrayMap<String, IpcDataCache.Config> sActionConfigMap = new ArrayMap<>();
+ @GuardedBy("BroadcastStickyCache.class")
private static final ArrayMap<StickyBroadcastFilter, IpcDataCache<Void, Intent>>
sFilterCacheMap = new ArrayMap<>();
@@ -154,37 +163,44 @@ public class BroadcastStickyCache {
@Nullable String broadcastPermission,
@UserIdInt int userId,
@RegisterReceiverFlags int flags) {
- IpcDataCache<Void, Intent> intentDataCache = findIpcDataCache(filter);
-
- if (intentDataCache == null) {
- final String action = filter.getAction(0);
- final StickyBroadcastFilter stickyBroadcastFilter =
- new StickyBroadcastFilter(filter, action);
- final Config config = getConfig(action);
-
- intentDataCache =
- new IpcDataCache<>(config,
- (query) -> ActivityManager.getService().registerReceiverWithFeature(
- applicationThread,
- mBasePackageName,
- attributionTag,
- /* receiverId= */ "null",
- /* receiver= */ null,
- filter,
- broadcastPermission,
- userId,
- flags));
- sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache);
+ IpcDataCache<Void, Intent> intentDataCache;
+
+ synchronized (BroadcastStickyCache.class) {
+ intentDataCache = findIpcDataCache(filter);
+
+ if (intentDataCache == null) {
+ final String action = filter.getAction(0);
+ final StickyBroadcastFilter stickyBroadcastFilter =
+ new StickyBroadcastFilter(filter, action);
+ final Config config = getConfig(action);
+
+ intentDataCache =
+ new IpcDataCache<>(config,
+ (query) -> ActivityManager.getService().registerReceiverWithFeature(
+ applicationThread,
+ mBasePackageName,
+ attributionTag,
+ /* receiverId= */ "null",
+ /* receiver= */ null,
+ filter,
+ broadcastPermission,
+ userId,
+ flags));
+ sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache);
+ }
}
return intentDataCache.query(null);
}
@VisibleForTesting
public static void clearCacheForTest() {
- sFilterCacheMap.clear();
+ synchronized (BroadcastStickyCache.class) {
+ sFilterCacheMap.clear();
+ }
}
@Nullable
+ @GuardedBy("BroadcastStickyCache.class")
private static IpcDataCache<Void, Intent> findIpcDataCache(
@NonNull IntentFilter filter) {
for (int i = sFilterCacheMap.size() - 1; i >= 0; i--) {
@@ -198,17 +214,56 @@ public class BroadcastStickyCache {
}
@NonNull
+ @GuardedBy("BroadcastStickyCache.class")
private static IpcDataCache.Config getConfig(@NonNull String action) {
if (!sActionConfigMap.containsKey(action)) {
// We only need 1 entry per cache but just to be on the safer side we are choosing 32
// although we don't expect more than 1.
sActionConfigMap.put(action,
- new Config(32, IpcDataCache.MODULE_SYSTEM, sActionApiNameMap.get(action)));
+ new Config(32, IpcDataCache.MODULE_SYSTEM,
+ sActionApiNameMap.get(action)).cacheNulls(true));
}
return sActionConfigMap.get(action);
}
+ public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd) {
+ if (!Flags.useStickyBcastCache()) {
+ return;
+ }
+ final PrintWriter pw = new FastPrintWriter(new FileOutputStream(pfd.getFileDescriptor()));
+ synchronized (BroadcastStickyCache.class) {
+ dumpCacheLocked(pw);
+ }
+ pw.flush();
+ }
+
+ @GuardedBy("BroadcastStickyCache.class")
+ private static void dumpCacheLocked(@NonNull PrintWriter pw) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(
+ pw, " " /* singleIndent */, " " /* prefix */);
+ ipw.println("Cached sticky broadcasts:");
+ ipw.increaseIndent();
+ final int count = sFilterCacheMap.size();
+ if (count == 0) {
+ ipw.println("<empty>");
+ } else {
+ for (int i = 0; i < count; ++i) {
+ final StickyBroadcastFilter stickyBroadcast = sFilterCacheMap.keyAt(i);
+ final IpcDataCache<Void, Intent> ipcDataCache = sFilterCacheMap.valueAt(i);
+ ipw.print("Entry #");
+ ipw.print(i);
+ ipw.println(":");
+ ipw.increaseIndent();
+ ipw.print("action", stickyBroadcast.action).println();
+ ipw.print("filter", stickyBroadcast.filter.toLongString()).println();
+ ipcDataCache.dumpCacheEntries(pw);
+ ipw.decreaseIndent();
+ }
+ }
+ ipw.decreaseIndent();
+ }
+
@VisibleForTesting
private record StickyBroadcastFilter(@NonNull IntentFilter filter, @NonNull String action) {
}
diff --git a/core/java/android/app/CameraCompatTaskInfo.java b/core/java/android/app/CameraCompatTaskInfo.java
index 432a0da15a47..845d2acbaf9d 100644
--- a/core/java/android/app/CameraCompatTaskInfo.java
+++ b/core/java/android/app/CameraCompatTaskInfo.java
@@ -16,11 +16,16 @@
package android.app;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -153,6 +158,27 @@ public class CameraCompatTaskInfo implements Parcelable {
+ "}";
}
+ /**
+ * Returns the sandboxed display rotation based on the given {@code cameraCompatMode}.
+ *
+ * <p>This will be what the app likely expects in its requested orientation while running on a
+ * device with portrait natural orientation: `CAMERA_COMPAT_FREEFORM_PORTRAIT_*` is 0, and
+ * `CAMERA_COMPAT_FREEFORM_LANDSCAPE_*` is 90.
+ *
+ * @return {@link WindowConfiguration#ROTATION_UNDEFINED} if not in camera compat mode.
+ */
+ @Surface.Rotation
+ public static int getDisplayRotationFromCameraCompatMode(@FreeformCameraCompatMode int
+ cameraCompatMode) {
+ return switch (cameraCompatMode) {
+ case CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
+ CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT -> ROTATION_0;
+ case CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE,
+ CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT -> ROTATION_90;
+ default -> ROTATION_UNDEFINED;
+ };
+ }
+
/** Human readable version of the freeform camera compat mode. */
@NonNull
public static String freeformCameraCompatModeToString(
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index dcbdc2348fbc..d8aa8b3df622 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2366,7 +2366,11 @@ class ContextImpl extends Context {
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
+ int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+ }
+ private int resolveDeviceIdForPermissionCheck(String permission) {
// When checking a device-aware permission on a remote device, if the permission is CAMERA
// or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
// device doesn't have capability fall back to checking permission on the default device.
@@ -2387,9 +2391,9 @@ class ContextImpl extends Context {
VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
if (virtualDevice != null) {
if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
- && !virtualDevice.hasCustomAudioInputSupport())
+ && !virtualDevice.hasCustomAudioInputSupport())
|| (Objects.equals(permission, Manifest.permission.CAMERA)
- && !virtualDevice.hasCustomCameraSupport())) {
+ && !virtualDevice.hasCustomCameraSupport())) {
deviceId = Context.DEVICE_ID_DEFAULT;
}
} else {
@@ -2400,8 +2404,7 @@ class ContextImpl extends Context {
}
}
}
-
- return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+ return deviceId;
}
/** @hide */
@@ -2503,6 +2506,16 @@ class ContextImpl extends Context {
message);
}
+ /** @hide */
+ @Override
+ public int getPermissionRequestState(String permission) {
+ Objects.requireNonNull(permission, "Permission name can't be null");
+ int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ PermissionManager permissionManager = getSystemService(PermissionManager.class);
+ return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
+ deviceId);
+ }
+
@Override
public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
try {
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 06d01ecfcf06..063501bf82a2 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -46,6 +46,8 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
@@ -183,4 +185,6 @@ oneway interface IApplicationThread {
void scheduleTimeoutService(IBinder token, int startId);
void scheduleTimeoutServiceForType(IBinder token, int startId, int fgsType);
void schedulePing(in RemoteCallback pong);
+ void getExecutableMethodFileOffsets(in MethodDescriptor methodDescriptor,
+ in IOffsetCallback resultCallback);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a4d8a5cd4673..17638ee76dba 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3221,7 +3221,6 @@ public class Notification implements Parcelable
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean containsCustomViews() {
return contentView != null
|| bigContentView != null
@@ -3235,7 +3234,6 @@ public class Notification implements Parcelable
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean hasTitle() {
return extras != null
&& (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))
@@ -3245,7 +3243,7 @@ public class Notification implements Parcelable
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
public boolean hasPromotableStyle() {
final Class<? extends Style> notificationStyle = getNotificationStyle();
@@ -3257,11 +3255,16 @@ public class Notification implements Parcelable
}
/**
- * @hide
+ * Returns whether the notification has all the characteristics that make it eligible for
+ * {@link #FLAG_PROMOTED_ONGOING}. This method does not factor in other criteria such user
+ * preferences for the app or channel. If this returns true, it does not guarantee that the
+ * notification will be assigned FLAG_PROMOTED_ONGOING by the system, but if this returns false,
+ * it will not.
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
public boolean hasPromotableCharacteristics() {
return isColorizedRequested()
+ && isOngoingEvent()
&& hasTitle()
&& !isGroupSummary()
&& !containsCustomViews()
@@ -3320,6 +3323,18 @@ public class Notification implements Parcelable
}
/**
+ * Make sure this String is safe to put into a bundle.
+ * @hide
+ */
+ public static String safeString(String str) {
+ if (str == null) return str;
+ if (str.length() > MAX_CHARSEQUENCE_LENGTH) {
+ str = str.substring(0, MAX_CHARSEQUENCE_LENGTH);
+ }
+ return str;
+ }
+
+ /**
* Make sure this CharSequence is safe to put into a bundle, which basically
* means it had better not be some custom Parcelable implementation.
* @hide
@@ -4158,6 +4173,13 @@ public class Notification implements Parcelable
/**
* @hide
*/
+ public boolean isOngoingEvent() {
+ return (flags & FLAG_ONGOING_EVENT) != 0;
+ }
+
+ /**
+ * @hide
+ */
public boolean hasCompletedProgress() {
// not a progress notification; can't be complete
if (!extras.containsKey(EXTRA_PROGRESS)
@@ -5041,7 +5063,7 @@ public class Notification implements Parcelable
@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
@NonNull
public Builder setShortCriticalText(@Nullable String shortCriticalText) {
- mN.extras.putString(EXTRA_SHORT_CRITICAL_TEXT, shortCriticalText);
+ mN.extras.putString(EXTRA_SHORT_CRITICAL_TEXT, safeString(shortCriticalText));
return this;
}
@@ -6014,8 +6036,9 @@ public class Notification implements Parcelable
/**
* @param isHeader If the notification is a notification header
* @return An instance of mColors after resolving the palette
+ * @hide
*/
- private Colors getColors(boolean isHeader) {
+ public Colors getColors(boolean isHeader) {
mColors.resolvePalette(mContext, mN.color, !isHeader && mN.isColorized(), mInNightMode);
return mColors;
}
@@ -14755,7 +14778,6 @@ public class Notification implements Parcelable
* A utility which stores and calculates the palette of colors used to color notifications.
* @hide
*/
- @VisibleForTesting
public static class Colors {
private int mPaletteIsForRawColor = COLOR_INVALID;
private boolean mPaletteIsForColorized = false;
@@ -14829,10 +14851,7 @@ public class Notification implements Parcelable
if (isColorized) {
if (rawColor == COLOR_DEFAULT) {
- int[] attrs = {R.attr.materialColorSecondary};
- try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
- mBackgroundColor = getColor(ta, 0, Color.WHITE);
- }
+ mBackgroundColor = ctx.getColor(R.color.materialColorSecondary);
} else {
mBackgroundColor = rawColor;
}
@@ -14864,30 +14883,25 @@ public class Notification implements Parcelable
mRippleAlpha = 0x33;
} else {
int[] attrs = {
- R.attr.materialColorSurfaceContainerHigh,
- R.attr.materialColorOnSurface,
- R.attr.materialColorOnSurfaceVariant,
- R.attr.materialColorPrimary,
- R.attr.materialColorSecondary,
- R.attr.materialColorTertiary,
- R.attr.materialColorOnTertiary,
- R.attr.materialColorTertiaryFixedDim,
- R.attr.materialColorOnTertiaryFixed,
R.attr.colorError,
R.attr.colorControlHighlight
};
+
+ mBackgroundColor = ctx.getColor(R.color.materialColorSurfaceContainerHigh);
+ mPrimaryTextColor = ctx.getColor(R.color.materialColorOnSurface);
+ mSecondaryTextColor = ctx.getColor(R.color.materialColorOnSurfaceVariant);
+ mPrimaryAccentColor = ctx.getColor(R.color.materialColorPrimary);
+ mSecondaryAccentColor = ctx.getColor(R.color.materialColorSecondary);
+ mTertiaryAccentColor = ctx.getColor(R.color.materialColorTertiary);
+ mOnTertiaryAccentTextColor = ctx.getColor(R.color.materialColorOnTertiary);
+ mTertiaryFixedDimAccentColor = ctx.getColor(
+ R.color.materialColorTertiaryFixedDim);
+ mOnTertiaryFixedAccentTextColor = ctx.getColor(
+ R.color.materialColorOnTertiaryFixed);
+
try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
- mBackgroundColor = getColor(ta, 0, nightMode ? Color.BLACK : Color.WHITE);
- mPrimaryTextColor = getColor(ta, 1, COLOR_INVALID);
- mSecondaryTextColor = getColor(ta, 2, COLOR_INVALID);
- mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID);
- mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID);
- mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID);
- mOnTertiaryAccentTextColor = getColor(ta, 6, COLOR_INVALID);
- mTertiaryFixedDimAccentColor = getColor(ta, 7, COLOR_INVALID);
- mOnTertiaryFixedAccentTextColor = getColor(ta, 8, COLOR_INVALID);
- mErrorColor = getColor(ta, 9, COLOR_INVALID);
- mRippleAlpha = Color.alpha(getColor(ta, 10, 0x33ffffff));
+ mErrorColor = getColor(ta, 0, COLOR_INVALID);
+ mRippleAlpha = Color.alpha(getColor(ta, 1, 0x33ffffff));
}
mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
mBackgroundColor, nightMode);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 87c861912036..8ed66eb7e6c0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -49,7 +49,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -61,15 +60,18 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Adjustment;
import android.service.notification.Condition;
+import android.service.notification.RateEstimator;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.util.Log;
+import android.util.LruCache;
import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.InstantSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -643,9 +645,17 @@ public class NotificationManager {
*/
public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500;
+ private static final float MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
+
+ private final Context mContext;
private final Map<CallNotificationEventListener, CallNotificationEventCallbackStub>
mCallNotificationEventCallbacks = new HashMap<>();
+ private final InstantSource mClock;
+ private final RateEstimator mEnqueueRateEstimator = new RateEstimator();
+ private final LruCache<String, Boolean> mEnqueuedNotificationKeys = new LruCache<>(10);
+ private final Object mEnqueueThrottleLock = new Object();
+
@UnsupportedAppUsage
private static INotificationManager sService;
@@ -661,10 +671,17 @@ public class NotificationManager {
return sService;
}
+ /** @hide */
+ protected INotificationManager service() {
+ return getService();
+ }
+
+ /** {@hide} */
@UnsupportedAppUsage
- /*package*/ NotificationManager(Context context, Handler handler)
+ public NotificationManager(Context context, InstantSource clock)
{
mContext = context;
+ mClock = clock;
}
/** {@hide} */
@@ -736,7 +753,7 @@ public class NotificationManager {
*/
public void notifyAsPackage(@NonNull String targetPackage, @Nullable String tag, int id,
@NonNull Notification notification) {
- INotificationManager service = getService();
+ INotificationManager service = service();
String sender = mContext.getPackageName();
try {
@@ -752,13 +769,12 @@ public class NotificationManager {
* @hide
*/
@UnsupportedAppUsage
- public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
+ public void notifyAsUser(@Nullable String tag, int id, Notification notification,
+ UserHandle user)
{
- INotificationManager service = getService();
+ INotificationManager service = service();
String pkg = mContext.getPackageName();
-
- if (notificationClassification()
- && NotificationChannel.SYSTEM_RESERVED_IDS.contains(notification.getChannelId())) {
+ if (discardNotify(tag, id, notification)) {
return;
}
@@ -771,6 +787,37 @@ public class NotificationManager {
}
}
+ /**
+ * Determines whether a {@link #notify} call should be skipped. If the notification is not
+ * skipped, updates tracking metadata to use in future decisions.
+ */
+ private boolean discardNotify(@Nullable String tag, int id, Notification notification) {
+ if (notificationClassification()
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(notification.getChannelId())) {
+ return true;
+ }
+
+ if (Flags.nmBinderPerfThrottleNotify()) {
+ String key = toEnqueuedNotificationKey(tag, id);
+ long now = mClock.millis();
+ synchronized (mEnqueueThrottleLock) {
+ if (mEnqueuedNotificationKeys.get(key) != null
+ && !notification.hasCompletedProgress()
+ && mEnqueueRateEstimator.getRate(now) > MAX_NOTIFICATION_ENQUEUE_RATE) {
+ return true;
+ }
+
+ mEnqueueRateEstimator.update(now);
+ mEnqueuedNotificationKeys.put(key, Boolean.TRUE);
+ }
+ }
+
+ return false;
+ }
+ private static String toEnqueuedNotificationKey(@Nullable String tag, int id) {
+ return tag + "," + id;
+ }
+
private Notification fixNotification(Notification notification) {
String pkg = mContext.getPackageName();
// Fix the notification as best we can.
@@ -852,7 +899,7 @@ public class NotificationManager {
* @param id An identifier for this notification.
*/
public void cancelAsPackage(@NonNull String targetPackage, @Nullable String tag, int id) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.cancelNotificationWithTag(targetPackage, mContext.getOpPackageName(),
tag, id, mContext.getUser().getIdentifier());
@@ -865,9 +912,15 @@ public class NotificationManager {
* @hide
*/
@UnsupportedAppUsage
- public void cancelAsUser(String tag, int id, UserHandle user)
+ public void cancelAsUser(@Nullable String tag, int id, UserHandle user)
{
- INotificationManager service = getService();
+ if (Flags.nmBinderPerfThrottleNotify()) {
+ synchronized (mEnqueueThrottleLock) {
+ mEnqueuedNotificationKeys.remove(toEnqueuedNotificationKey(tag, id));
+ }
+ }
+
+ INotificationManager service = service();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
try {
@@ -884,7 +937,13 @@ public class NotificationManager {
*/
public void cancelAll()
{
- INotificationManager service = getService();
+ if (Flags.nmBinderPerfThrottleNotify()) {
+ synchronized (mEnqueueThrottleLock) {
+ mEnqueuedNotificationKeys.evictAll();
+ }
+ }
+
+ INotificationManager service = service();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
@@ -907,7 +966,7 @@ public class NotificationManager {
* @param delegate Package name of the app which can send notifications on your behalf.
*/
public void setNotificationDelegate(@Nullable String delegate) {
- INotificationManager service = getService();
+ INotificationManager service = service();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
@@ -922,7 +981,7 @@ public class NotificationManager {
* your behalf, if there currently is one.
*/
public @Nullable String getNotificationDelegate() {
- INotificationManager service = getService();
+ INotificationManager service = service();
String pkg = mContext.getPackageName();
try {
return service.getNotificationDelegate(pkg);
@@ -938,7 +997,7 @@ public class NotificationManager {
* See {@link #setNotificationDelegate(String)}.
*/
public boolean canNotifyAsPackage(@NonNull String pkg) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.canNotifyAsPackage(mContext.getPackageName(), pkg, mContext.getUserId());
} catch (RemoteException e) {
@@ -956,7 +1015,7 @@ public class NotificationManager {
* {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT}.
*/
public boolean canUseFullScreenIntent() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.canUseFullScreenIntent(mContext.getAttributionSource());
} catch (RemoteException e) {
@@ -974,7 +1033,7 @@ public class NotificationManager {
*/
@FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
public boolean canPostPromotedNotifications() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.canBePromoted(mContext.getPackageName());
} catch (RemoteException e) {
@@ -989,7 +1048,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
public void setCanPostPromotedNotifications(@NonNull String pkg, int uid, boolean allowed) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setCanBePromoted(pkg, uid, allowed, true);
} catch (RemoteException e) {
@@ -1024,7 +1083,7 @@ public class NotificationManager {
* @param groups The list of groups to create
*/
public void createNotificationChannelGroups(@NonNull List<NotificationChannelGroup> groups) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.createNotificationChannelGroups(mContext.getPackageName(),
new ParceledListSlice(groups));
@@ -1067,7 +1126,7 @@ public class NotificationManager {
* @param channels the list of channels to attempt to create.
*/
public void createNotificationChannels(@NonNull List<NotificationChannel> channels) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.createNotificationChannels(mContext.getPackageName(),
new ParceledListSlice(channels));
@@ -1085,7 +1144,7 @@ public class NotificationManager {
* package (see {@link Context#createPackageContext(String, int)}).</p>
*/
public NotificationChannel getNotificationChannel(String channelId) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getNotificationChannel(mContext.getOpPackageName(),
mContext.getUserId(), mContext.getPackageName(), channelId);
@@ -1105,7 +1164,7 @@ public class NotificationManager {
*/
public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId,
@NonNull String conversationId) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getConversationNotificationChannel(mContext.getOpPackageName(),
mContext.getUserId(), mContext.getPackageName(), channelId, true,
@@ -1124,7 +1183,7 @@ public class NotificationManager {
* {@link Context#createPackageContext(String, int)}).</p>
*/
public List<NotificationChannel> getNotificationChannels() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getNotificationChannels(mContext.getOpPackageName(),
mContext.getPackageName(), mContext.getUserId()).getList();
@@ -1145,7 +1204,7 @@ public class NotificationManager {
&& NotificationChannel.SYSTEM_RESERVED_IDS.contains(channelId)) {
return;
}
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.deleteNotificationChannel(mContext.getPackageName(), channelId);
} catch (RemoteException e) {
@@ -1159,7 +1218,7 @@ public class NotificationManager {
* The channel group must belong to your package, or null will be returned.
*/
public NotificationChannelGroup getNotificationChannelGroup(String channelGroupId) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getNotificationChannelGroup(mContext.getPackageName(), channelGroupId);
} catch (RemoteException e) {
@@ -1171,7 +1230,7 @@ public class NotificationManager {
* Returns all notification channel groups belonging to the calling app.
*/
public List<NotificationChannelGroup> getNotificationChannelGroups() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
final ParceledListSlice<NotificationChannelGroup> parceledList =
service.getNotificationChannelGroups(mContext.getPackageName());
@@ -1189,7 +1248,7 @@ public class NotificationManager {
* belong to it.
*/
public void deleteNotificationChannelGroup(String groupId) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.deleteNotificationChannelGroup(mContext.getPackageName(), groupId);
} catch (RemoteException e) {
@@ -1203,7 +1262,7 @@ public class NotificationManager {
@TestApi
public void updateNotificationChannel(@NonNull String pkg, int uid,
@NonNull NotificationChannel channel) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.updateNotificationChannelForPackage(pkg, uid, channel);
} catch (RemoteException e) {
@@ -1216,7 +1275,7 @@ public class NotificationManager {
*/
@TestApi
public ComponentName getEffectsSuppressor() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getEffectsSuppressor();
} catch (RemoteException e) {
@@ -1228,7 +1287,7 @@ public class NotificationManager {
* @hide
*/
public boolean matchesCallFilter(Bundle extras) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.matchesCallFilter(extras);
} catch (RemoteException e) {
@@ -1241,7 +1300,7 @@ public class NotificationManager {
*/
@TestApi
public void cleanUpCallersAfter(long timeThreshold) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.cleanUpCallersAfter(timeThreshold);
} catch (RemoteException e) {
@@ -1253,7 +1312,7 @@ public class NotificationManager {
* @hide
*/
public boolean isSystemConditionProviderEnabled(String path) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isSystemConditionProviderEnabled(path);
} catch (RemoteException e) {
@@ -1271,7 +1330,7 @@ public class NotificationManager {
/** @hide */
public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setZenMode(mode, conditionId, reason, fromUser);
} catch (RemoteException e) {
@@ -1284,7 +1343,7 @@ public class NotificationManager {
* @hide
*/
public int getZenMode() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getZenMode();
} catch (RemoteException e) {
@@ -1297,7 +1356,7 @@ public class NotificationManager {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ZenModeConfig getZenModeConfig() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getZenModeConfig();
} catch (RemoteException e) {
@@ -1315,7 +1374,7 @@ public class NotificationManager {
* </p>
*/
public @NonNull NotificationManager.Policy getConsolidatedNotificationPolicy() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getConsolidatedNotificationPolicy();
} catch (RemoteException e) {
@@ -1327,7 +1386,7 @@ public class NotificationManager {
* @hide
*/
public int getRuleInstanceCount(ComponentName owner) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getRuleInstanceCount(owner);
} catch (RemoteException e) {
@@ -1364,7 +1423,7 @@ public class NotificationManager {
* See {@link #isNotificationPolicyAccessGranted}.
*/
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
if (Flags.modesApi()) {
return service.getAutomaticZenRules();
@@ -1398,7 +1457,7 @@ public class NotificationManager {
* doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
*/
public AutomaticZenRule getAutomaticZenRule(String id) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getAutomaticZenRule(id);
} catch (RemoteException e) {
@@ -1426,7 +1485,7 @@ public class NotificationManager {
@NonNull
public String addAutomaticZenRule(@NonNull AutomaticZenRule automaticZenRule,
boolean fromUser) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.addAutomaticZenRule(automaticZenRule,
mContext.getPackageName(), fromUser);
@@ -1461,7 +1520,7 @@ public class NotificationManager {
@FlaggedApi(Flags.FLAG_MODES_API)
public boolean updateAutomaticZenRule(@NonNull String id,
@NonNull AutomaticZenRule automaticZenRule, boolean fromUser) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.updateAutomaticZenRule(id, automaticZenRule, fromUser);
} catch (RemoteException e) {
@@ -1481,7 +1540,7 @@ public class NotificationManager {
@FlaggedApi(Flags.FLAG_MODES_API)
@Condition.State
public int getAutomaticZenRuleState(@NonNull String id) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getAutomaticZenRuleState(id);
} catch (RemoteException e) {
@@ -1527,7 +1586,7 @@ public class NotificationManager {
* @param condition The new state of this rule
*/
public void setAutomaticZenRuleState(@NonNull String id, @NonNull Condition condition) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setAutomaticZenRuleState(id, condition);
} catch (RemoteException e) {
@@ -1555,7 +1614,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(Flags.FLAG_MODES_API)
public boolean removeAutomaticZenRule(@NonNull String id, boolean fromUser) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.removeAutomaticZenRule(id, fromUser);
} catch (RemoteException e) {
@@ -1574,7 +1633,7 @@ public class NotificationManager {
/** @hide */
public boolean removeAutomaticZenRules(String packageName, boolean fromUser) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.removeAutomaticZenRules(packageName, fromUser);
} catch (RemoteException e) {
@@ -1587,7 +1646,7 @@ public class NotificationManager {
* package.
*/
public @Importance int getImportance() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getPackageImportance(mContext.getPackageName());
} catch (RemoteException e) {
@@ -1602,7 +1661,7 @@ public class NotificationManager {
if (Flags.nmBinderPerfPermissionCheck()) {
return mContext.checkSelfPermission(POST_NOTIFICATIONS) == PERMISSION_GRANTED;
} else {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.areNotificationsEnabled(mContext.getPackageName());
} catch (RemoteException e) {
@@ -1623,7 +1682,7 @@ public class NotificationManager {
*/
@Deprecated
public boolean areBubblesAllowed() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.areBubblesAllowed(mContext.getPackageName());
} catch (RemoteException e) {
@@ -1638,7 +1697,7 @@ public class NotificationManager {
* @see Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata)
*/
public boolean areBubblesEnabled() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.areBubblesEnabled(mContext.getUser());
} catch (RemoteException e) {
@@ -1665,7 +1724,7 @@ public class NotificationManager {
* @return the users' bubble preference for the app.
*/
public @BubblePreference int getBubblePreference() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getBubblePreferenceForPackage(mContext.getPackageName(),
Binder.getCallingUid());
@@ -1685,7 +1744,7 @@ public class NotificationManager {
* @hide
*/
public void silenceNotificationSound() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.silenceNotificationSound();
} catch (RemoteException e) {
@@ -1701,7 +1760,7 @@ public class NotificationManager {
* PersistableBundle, SuspendDialogInfo) suspended}.
*/
public boolean areNotificationsPaused() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isPackagePaused(mContext.getPackageName());
} catch (RemoteException e) {
@@ -1724,7 +1783,7 @@ public class NotificationManager {
* user grant or denial of this access.
*/
public boolean isNotificationPolicyAccessGranted() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isNotificationPolicyAccessGranted(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1745,7 +1804,7 @@ public class NotificationManager {
* {@link android.provider.Settings#ACTION_NOTIFICATION_LISTENER_SETTINGS}.
*/
public boolean isNotificationListenerAccessGranted(ComponentName listener) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isNotificationListenerAccessGranted(listener);
} catch (RemoteException e) {
@@ -1769,7 +1828,7 @@ public class NotificationManager {
*/
@SystemApi
public boolean isNotificationAssistantAccessGranted(@NonNull ComponentName assistant) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isNotificationAssistantAccessGranted(assistant);
} catch (RemoteException e) {
@@ -1785,7 +1844,7 @@ public class NotificationManager {
* listeners}.
*/
public boolean shouldHideSilentStatusBarIcons() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.shouldHideSilentStatusIcons(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1804,7 +1863,7 @@ public class NotificationManager {
*/
@SystemApi
public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getAllowedAssistantAdjustments(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1818,7 +1877,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void allowAssistantAdjustment(@NonNull String capability) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.allowAssistantAdjustment(capability);
} catch (RemoteException e) {
@@ -1832,7 +1891,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void disallowAssistantAdjustment(@NonNull String capability) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.disallowAssistantAdjustment(capability);
} catch (RemoteException e) {
@@ -1843,7 +1902,7 @@ public class NotificationManager {
/** @hide */
@TestApi
public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String pkg) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.isNotificationPolicyAccessGrantedForPackage(pkg);
} catch (RemoteException e) {
@@ -1857,7 +1916,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setAssistantAdjustmentKeyTypeState(type, enabled);
} catch (RemoteException e) {
@@ -1870,7 +1929,7 @@ public class NotificationManager {
*/
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setTypeAdjustmentForPackageState(pkg, enabled);
} catch (RemoteException e) {
@@ -1882,7 +1941,7 @@ public class NotificationManager {
* @hide
*/
public List<String> getEnabledNotificationListenerPackages() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getEnabledNotificationListenerPackages();
} catch (RemoteException e) {
@@ -1899,7 +1958,7 @@ public class NotificationManager {
* {@link #setNotificationPolicy(Policy)}.
*/
public Policy getNotificationPolicy() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getNotificationPolicy(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1929,7 +1988,7 @@ public class NotificationManager {
/** @hide */
public void setNotificationPolicy(@NonNull Policy policy, boolean fromUser) {
checkRequired("policy", policy);
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setNotificationPolicy(mContext.getOpPackageName(), policy, fromUser);
} catch (RemoteException e) {
@@ -1939,7 +1998,7 @@ public class NotificationManager {
/** @hide */
public void setNotificationPolicyAccessGranted(String pkg, boolean granted) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setNotificationPolicyAccessGranted(pkg, granted);
} catch (RemoteException e) {
@@ -1959,7 +2018,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(Flags.FLAG_MODES_API)
public @NonNull ZenPolicy getDefaultZenPolicy() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getDefaultZenPolicy();
} catch (RemoteException e) {
@@ -1971,7 +2030,7 @@ public class NotificationManager {
*/
@FlaggedApi(Flags.FLAG_MODES_UI)
public void setManualZenRuleDeviceEffects(@NonNull ZenDeviceEffects effects) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setManualZenRuleDeviceEffects(effects);
} catch (RemoteException e) {
@@ -2008,7 +2067,7 @@ public class NotificationManager {
@RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
public void setNotificationListenerAccessGranted(
@NonNull ComponentName listener, boolean granted, boolean userSet) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
if (CompatChanges.isChangeEnabled(SET_LISTENER_ACCESS_GRANTED_IS_USER_AWARE)) {
service.setNotificationListenerAccessGrantedForUser(listener, mContext.getUserId(),
@@ -2024,7 +2083,7 @@ public class NotificationManager {
/** @hide */
public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
boolean granted) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setNotificationListenerAccessGrantedForUser(listener, userId, granted, true);
} catch (RemoteException e) {
@@ -2045,7 +2104,7 @@ public class NotificationManager {
@SystemApi
public void setNotificationAssistantAccessGranted(@Nullable ComponentName assistant,
boolean granted) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setNotificationAssistantAccessGranted(assistant, granted);
} catch (RemoteException e) {
@@ -2069,7 +2128,7 @@ public class NotificationManager {
/** @hide */
public List<ComponentName> getEnabledNotificationListeners(int userId) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getEnabledNotificationListeners(userId);
} catch (RemoteException e) {
@@ -2080,7 +2139,7 @@ public class NotificationManager {
/** @hide */
@SystemApi
public @Nullable ComponentName getAllowedNotificationAssistant() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.getAllowedNotificationAssistant();
} catch (RemoteException e) {
@@ -2100,16 +2159,13 @@ public class NotificationManager {
@SuppressLint("UserHandle")
public boolean hasEnabledNotificationListener(@NonNull String packageName,
@NonNull UserHandle userHandle) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
-
- private Context mContext;
-
private static void checkRequired(String name, Object value) {
if (value == null) {
throw new IllegalArgumentException(name + " is required");
@@ -2125,7 +2181,7 @@ public class NotificationManager {
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING)
public void setToastRateLimitingEnabled(boolean enable) {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
service.setToastRateLimitingEnabled(enable);
} catch (RemoteException e) {
@@ -2917,7 +2973,7 @@ public class NotificationManager {
* @return An array of {@link StatusBarNotification}.
*/
public StatusBarNotification[] getActiveNotifications() {
- final INotificationManager service = getService();
+ final INotificationManager service = service();
final String pkg = mContext.getPackageName();
try {
final ParceledListSlice<StatusBarNotification> parceledList
@@ -2940,7 +2996,7 @@ public class NotificationManager {
* globally.
*/
public final @InterruptionFilter int getCurrentInterruptionFilter() {
- final INotificationManager service = getService();
+ final INotificationManager service = service();
try {
return zenModeToInterruptionFilter(service.getZenMode());
} catch (RemoteException e) {
@@ -2972,7 +3028,7 @@ public class NotificationManager {
/** @hide */
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter,
boolean fromUser) {
- final INotificationManager service = getService();
+ final INotificationManager service = service();
try {
service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter,
fromUser);
@@ -3130,7 +3186,7 @@ public class NotificationManager {
checkRequired("userHandle", userHandle);
checkRequired("executor", executor);
checkRequired("listener", listener);
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
synchronized (mCallNotificationEventCallbacks) {
CallNotificationEventCallbackStub callbackStub =
@@ -3161,7 +3217,7 @@ public class NotificationManager {
public void unregisterCallNotificationEventListener(
@NonNull CallNotificationEventListener listener) {
checkRequired("listener", listener);
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
synchronized (mCallNotificationEventCallbacks) {
CallNotificationEventCallbackStub callbackStub =
@@ -3184,7 +3240,7 @@ public class NotificationManager {
@TestApi
@FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public @NonNull Set<String> getUnsupportedAdjustmentTypes() {
- INotificationManager service = getService();
+ INotificationManager service = service();
try {
return new HashSet<>(service.getUnsupportedAdjustmentTypes());
} catch (RemoteException e) {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 3973c58c0708..5567c085d205 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -17,13 +17,13 @@
package android.app;
import static android.text.TextUtils.formatSimple;
+
import static com.android.internal.util.Preconditions.checkArgumentPositive;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -80,6 +80,17 @@ import java.util.concurrent.atomic.AtomicLong;
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PropertyInvalidatedCache<Query, Result> {
/**
+ * A method to report if the PermissionManager notifications can be separated from cache
+ * invalidation. The feature relies on a series of flags; the dependency is captured in this
+ * method.
+ * @hide
+ */
+ public static boolean separatePermissionNotificationsEnabled() {
+ return isSharedMemoryAvailable()
+ && Flags.picSeparatePermissionNotifications();
+ }
+
+ /**
* This is a configuration class that customizes a cache instance.
* @hide
*/
@@ -1283,6 +1294,13 @@ public class PropertyInvalidatedCache<Query, Result> {
public static record Args(@NonNull String mModule, @Nullable String mApi,
int mMaxEntries, boolean mIsolateUids, boolean mTestMode, boolean mCacheNulls) {
+ /**
+ * Default values for fields.
+ */
+ public static final int DEFAULT_MAX_ENTRIES = 32;
+ public static final boolean DEFAULT_ISOLATE_UIDS = true;
+ public static final boolean DEFAULT_CACHE_NULLS = false;
+
// Validation: the module must be one of the known module strings and the maxEntries must
// be positive.
public Args {
@@ -1297,10 +1315,10 @@ public class PropertyInvalidatedCache<Query, Result> {
public Args(@NonNull String module) {
this(module,
null, // api
- 32, // maxEntries
- true, // isolateUids
+ DEFAULT_MAX_ENTRIES,
+ DEFAULT_ISOLATE_UIDS,
false, // testMode
- true // allowNulls
+ DEFAULT_CACHE_NULLS
);
}
@@ -1338,7 +1356,7 @@ public class PropertyInvalidatedCache<Query, Result> {
@Nullable QueryHandler<Query, Result> computer) {
mPropertyName = createPropertyName(args.mModule, args.mApi);
mCacheName = cacheName;
- mCacheNullResults = args.mCacheNulls && Flags.picCacheNulls();
+ mCacheNullResults = args.mCacheNulls;
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = args.mMaxEntries;
mCache = new CacheMap<>(args.mIsolateUids, args.mTestMode);
@@ -1350,7 +1368,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* Burst a property name into module and api. Throw if the key is invalid. This method is
* used in to transition legacy cache constructors to the args constructor.
*/
- private static Args parseProperty(@NonNull String name) {
+ private static Args argsFromProperty(@NonNull String name) {
throwIfInvalidCacheKey(name);
// Strip off the leading well-known prefix.
String base = name.substring(CACHE_KEY_PREFIX.length() + 1);
@@ -1373,8 +1391,9 @@ public class PropertyInvalidatedCache<Query, Result> {
*
* @hide
*/
+ @Deprecated
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
- this(parseProperty(propertyName).maxEntries(maxEntries), propertyName, null);
+ this(argsFromProperty(propertyName).maxEntries(maxEntries), propertyName, null);
}
/**
@@ -1388,9 +1407,10 @@ public class PropertyInvalidatedCache<Query, Result> {
* @param cacheName Name of this cache in debug and dumpsys
* @hide
*/
+ @Deprecated
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
- this(parseProperty(propertyName).maxEntries(maxEntries), cacheName, null);
+ this(argsFromProperty(propertyName).maxEntries(maxEntries), cacheName, null);
}
/**
@@ -1846,6 +1866,14 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * Invalidate caches in all processes that have the module and api specified in the args.
+ * @hide
+ */
+ public static void invalidateCache(@NonNull Args args) {
+ invalidateCache(createPropertyName(args.mModule, args.mApi));
+ }
+
+ /**
* Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
* {@var name}. This function is synchronous: caches are invalidated upon return.
*
@@ -1921,6 +1949,10 @@ public class PropertyInvalidatedCache<Query, Result> {
}
public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
+ if (separatePermissionNotificationsEnabled()) {
+ throw new IllegalStateException("AutoCorking is unavailable");
+ }
+
mPropertyName = propertyName;
mAutoCorkDelayMs = autoCorkDelayMs;
// We can't initialize mHandler here: when we're created, the main loop might not
@@ -2294,6 +2326,19 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * This dumps the detailed entries (Query and Result) inside the current instance of the
+ * {@link PropertyInvalidatedCache}.
+ *
+ * @param pw The PrintWriter object for the output stream.
+ * @hide
+ */
+ public void dumpCacheEntries(@NonNull PrintWriter pw) {
+ synchronized (mLock) {
+ mCache.dumpDetailed(pw);
+ }
+ }
+
+ /**
* Nonces in shared memory are supported by a string block that acts as a table of contents
* for nonce names, and an array of nonce values. There are two key design principles with
* respect to nonce maps:
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 3cffca796680..51d0b18467f4 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1517,10 +1517,8 @@ public class ResourcesManager {
int changes = mResConfiguration.updateFrom(config);
if (compat != null && (mResCompatibilityInfo == null
|| !mResCompatibilityInfo.equals(compat))) {
+ changes |= compat.getCompatibilityChangesForConfig(mResCompatibilityInfo);
mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
// If a application info update was scheduled to occur in this process but has not
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 2bd2d34d54a2..920b19cd8f78 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -42,8 +42,7 @@ import android.app.contentsuggestions.IContentSuggestionsManager;
import android.app.contextualsearch.ContextualSearchManager;
import android.app.ecm.EnhancedConfirmationFrameworkInitializer;
import android.app.job.JobSchedulerFrameworkInitializer;
-import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
-import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceFrameworkInitializer;
import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionManager;
import android.app.role.RoleFrameworkInitializer;
@@ -82,8 +81,6 @@ import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
-import android.content.integrity.AppIntegrityManager;
-import android.content.integrity.IAppIntegrityManager;
import android.content.om.IOverlayManager;
import android.content.om.OverlayManager;
import android.content.pm.ApplicationInfo;
@@ -292,6 +289,7 @@ import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
+import java.time.InstantSource;
import java.util.Map;
import java.util.Objects;
@@ -628,7 +626,7 @@ public final class SystemServiceRegistry {
com.android.internal.R.style.Theme_Holo_Dialog,
com.android.internal.R.style.Theme_DeviceDefault_Dialog,
com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
- ctx.mMainThread.getHandler());
+ InstantSource.system());
}});
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
@@ -1581,16 +1579,6 @@ public final class SystemServiceRegistry {
return new AttestationVerificationManager(ctx.getOuterContext(),
IAttestationVerificationManagerService.Stub.asInterface(b));
}});
-
- //CHECKSTYLE:ON IndentationCheck
- registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
- new CachedServiceFetcher<AppIntegrityManager>() {
- @Override
- public AppIntegrityManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
- return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
- }});
registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class,
new CachedServiceFetcher<AppHibernationManager>() {
@Override
@@ -1704,19 +1692,6 @@ public final class SystemServiceRegistry {
throw new ServiceNotFoundException(Context.WEARABLE_SENSING_SERVICE);
}});
- registerService(Context.ON_DEVICE_INTELLIGENCE_SERVICE, OnDeviceIntelligenceManager.class,
- new CachedServiceFetcher<OnDeviceIntelligenceManager>() {
- @Override
- public OnDeviceIntelligenceManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder iBinder = ServiceManager.getServiceOrThrow(
- Context.ON_DEVICE_INTELLIGENCE_SERVICE);
- IOnDeviceIntelligenceManager manager =
- IOnDeviceIntelligenceManager.Stub.asInterface(iBinder);
- return new OnDeviceIntelligenceManager(ctx.getOuterContext(), manager);
- }
- });
-
registerService(Context.GRAMMATICAL_INFLECTION_SERVICE, GrammaticalInflectionManager.class,
new CachedServiceFetcher<GrammaticalInflectionManager>() {
@Override
@@ -1861,6 +1836,7 @@ public final class SystemServiceRegistry {
ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
NearbyFrameworkInitializer.registerServiceWrappers();
OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers();
+ OnDeviceIntelligenceFrameworkInitializer.registerServiceWrappers();
DeviceLockFrameworkInitializer.registerServiceWrappers();
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 637187e01160..e93d8bdb9c57 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -157,6 +157,10 @@
"file_patterns": ["(/|^)ContextImpl.java"]
},
{
+ "name": "BroadcastUnitTests",
+ "file_patterns": ["(/|^)BroadcastStickyCache.java"]
+ },
+ {
"file_patterns": [
"(/|^)Activity.*.java",
"(/|^)PendingIntent.java",
@@ -177,10 +181,6 @@
{
"file_patterns": ["(/|^)AppOpsManager.java"],
"name": "CtsAppOpsTestCases"
- },
- {
- "file_patterns": ["(/|^)BroadcastStickyCache.java"],
- "name": "BroadcastUnitTests"
}
]
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 01cc9d82d56d..76705dcdd3d2 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -308,12 +308,18 @@ public class TaskInfo {
public boolean isSleeping;
/**
- * Whether the top activity fillsParent() is false
+ * Whether the top activity fillsParent() is false.
* @hide
*/
public boolean isTopActivityTransparent;
/**
+ * Whether fillsParent() is false for every activity in the tasks stack.
+ * @hide
+ */
+ public boolean isActivityStackTransparent;
+
+ /**
* The last non-fullscreen bounds the task was launched in or resized to.
* @hide
*/
@@ -489,6 +495,7 @@ public class TaskInfo {
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
&& isTopActivityTransparent == that.isTopActivityTransparent
+ && isActivityStackTransparent == that.isActivityStackTransparent
&& Objects.equals(lastNonFullscreenBounds, that.lastNonFullscreenBounds)
&& Objects.equals(capturedLink, that.capturedLink)
&& capturedLinkTimestamp == that.capturedLinkTimestamp
@@ -567,6 +574,7 @@ public class TaskInfo {
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
isTopActivityTransparent = source.readBoolean();
+ isActivityStackTransparent = source.readBoolean();
lastNonFullscreenBounds = source.readTypedObject(Rect.CREATOR);
capturedLink = source.readTypedObject(Uri.CREATOR);
capturedLinkTimestamp = source.readLong();
@@ -623,6 +631,7 @@ public class TaskInfo {
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
dest.writeBoolean(isTopActivityTransparent);
+ dest.writeBoolean(isActivityStackTransparent);
dest.writeTypedObject(lastNonFullscreenBounds, flags);
dest.writeTypedObject(capturedLink, flags);
dest.writeLong(capturedLinkTimestamp);
@@ -668,6 +677,7 @@ public class TaskInfo {
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " isTopActivityTransparent=" + isTopActivityTransparent
+ + " isActivityStackTransparent=" + isActivityStackTransparent
+ " lastNonFullscreenBounds=" + lastNonFullscreenBounds
+ " capturedLink=" + capturedLink
+ " capturedLinkTimestamp=" + capturedLinkTimestamp
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 89e25e7d1b4f..360376da618c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2730,6 +2730,7 @@ public class WallpaperManager {
* @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ * @return An integer ID assigned to the newly active wallpaper; or zero on failure.
* @hide
*/
@FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 44940aee6a49..720e045dc944 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -53,16 +53,6 @@ flag {
flag {
namespace: "backstage_power"
- name: "gate_fgs_timeout_anr_behavior"
- description: "Gate the new behavior where an ANR is thrown once an FGS times out."
- bug: "339315145"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "backstage_power"
name: "enable_fgs_timeout_crash_behavior"
description: "Enable the new behavior where the app is crashed once an FGS times out."
bug: "339526947"
@@ -166,3 +156,10 @@ flag {
bug: "362537357"
is_exported: true
}
+
+flag {
+ name: "jank_perceptible_narrow"
+ namespace: "system_performance"
+ description: "Narrow the scope of Jank Perceptible"
+ bug: "304837972"
+}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 35149b5a3135..89261a4f85ee 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.appfunctions.flags.Flags;
import android.os.UserManager;
import java.util.Objects;
@@ -181,6 +182,12 @@ public final class DevicePolicyIdentifiers {
public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
/**
+ * String identifier for {@link DevicePolicyManager#setAppFunctionsPolicy(int)}.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final String APP_FUNCTIONS_POLICY = "appFunctions";
+
+ /**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8372078b46a5..9ddc729850c4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -23,6 +23,7 @@ import static android.Manifest.permission.LOCK_DEVICE;
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE;
@@ -3931,6 +3932,11 @@ public class DevicePolicyManager {
@FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41;
+ /** @hide */
+ @TestApi
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int OPERATION_SET_APP_FUNCTIONS_POLICY = 42;
+
private static final String PREFIX_OPERATION = "OPERATION_";
/** @hide */
@@ -3975,7 +3981,8 @@ public class DevicePolicyManager {
OPERATION_SET_PERMISSION_POLICY,
OPERATION_SET_RESTRICTIONS_PROVIDER,
OPERATION_UNINSTALL_CA_CERT,
- OPERATION_SET_CONTENT_PROTECTION_POLICY
+ OPERATION_SET_CONTENT_PROTECTION_POLICY,
+ OPERATION_SET_APP_FUNCTIONS_POLICY
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DevicePolicyOperation {
@@ -4347,6 +4354,90 @@ public class DevicePolicyManager {
}
/**
+ * Indicates that app functions are not controlled by policy.
+ *
+ * <p>If no admin set this policy, it means appfunctions are enabled.
+ */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY = 0;
+
+ /** Indicates that app functions are controlled and disabled by a policy. */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_DISABLED = 1;
+
+ /**
+ * Indicates that app functions are controlled and disabled by a policy for cross profile
+ * interactions only.
+ *
+ * <p>This is different from {@link #APP_FUNCTIONS_DISABLED} in that it only disables cross
+ * profile interactions (even if the caller has permissions required to interact across users).
+ * appfunctions can still be used within the a user profile boundary.
+ */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_DISABLED_CROSS_PROFILE = 2;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"APP_FUNCTIONS_"},
+ value = {
+ APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY,
+ APP_FUNCTIONS_DISABLED,
+ APP_FUNCTIONS_DISABLED_CROSS_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppFunctionsPolicy {}
+
+ /**
+ * Sets the app functions policy which controls app functions operations on the device.
+ *
+ * <p>This function can only be called by a device owner, a profile owner or holders of the
+ * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ *
+ * @param policy The app functions policy to set. One of {@link
+ * #APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY},
+ * {@link #APP_FUNCTIONS_DISABLED} or
+ * {@link #APP_FUNCTIONS_DISABLED_CROSS_PROFILE}
+ * @throws SecurityException if caller is not a device owner, a profile owner or a holder
+ * of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional = true)
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public void setAppFunctionsPolicy(@AppFunctionsPolicy int policy) {
+ throwIfParentInstance("setAppFunctionsPolicy");
+ if (mService != null) {
+ try {
+ mService.setAppFunctionsPolicy(mContext.getPackageName(), policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current app functions policy.
+ *
+ * <p>The returned policy will be the current resolved policy rather than the policy set by the
+ * calling admin.
+ *
+ * @throws SecurityException if caller is not a device owner, a profile owner or a holder
+ * of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional = true)
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public @AppFunctionsPolicy int getAppFunctionsPolicy() {
+ throwIfParentInstance("getAppFunctionsPolicy");
+ if (mService != null) {
+ try {
+ return mService.getAppFunctionsPolicy(mContext.getPackageName(),
+ myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+
+ /**
* This object is a single place to tack on invalidation and disable calls. All
* binder caches in this class derive from this Config, so all can be invalidated or
* disabled through this Config.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 03a9f9955086..f304c1bf0a88 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -644,4 +644,7 @@ interface IDevicePolicyManager {
int getPolicySizeForAdmin(String callerPackageName, in EnforcingAdmin admin);
int getHeadlessDeviceOwnerMode(String callerPackageName);
+
+ void setAppFunctionsPolicy(String callerPackageName, int policy);
+ int getAppFunctionsPolicy(String callerPackageName, int userId);
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 361ba73817c7..af035cb630dc 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -343,16 +343,6 @@ flag {
}
flag {
- name: "dont_write_policy_definition"
- namespace: "enterprise"
- description: "Don't write redundant policy-definition-entry tags"
- bug: "335663055"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "active_admin_cleanup"
namespace: "enterprise"
description: "Remove ActiveAdmin from EnforcingAdmin and related cleanups"
diff --git a/core/java/android/app/appfunctions/AppFunctionException.java b/core/java/android/app/appfunctions/AppFunctionException.java
index c8d80d3afe43..d8179c7540d9 100644
--- a/core/java/android/app/appfunctions/AppFunctionException.java
+++ b/core/java/android/app/appfunctions/AppFunctionException.java
@@ -32,8 +32,8 @@ import java.util.Objects;
/**
* Represents an app function related error.
*
- * <p>This exception may include an {@link AppFunctionException#getExtras() Bundle}
- * containing additional error-specific metadata.
+ * <p>This exception may include an {@link AppFunctionException#getExtras() Bundle} containing
+ * additional error-specific metadata.
*
* <p>The AppFunction SDK can expose structured APIs by packing and unpacking this Bundle.
*/
@@ -85,6 +85,13 @@ public final class AppFunctionException extends Exception implements Parcelable
public static final int ERROR_CANCELLED = 2001;
/**
+ * The operation was disallowed by enterprise policy.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002;
+
+ /**
* An unknown error occurred while processing the call in the AppFunctionService.
*
* <p>This error is thrown when the service is connected in the remote application but an
@@ -231,7 +238,8 @@ public final class AppFunctionException extends Exception implements Parcelable
ERROR_SYSTEM_ERROR,
ERROR_INVALID_ARGUMENT,
ERROR_DISABLED,
- ERROR_CANCELLED
+ ERROR_CANCELLED,
+ ERROR_ENTERPRISE_POLICY_DISALLOWED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index ed088fed41c2..a731e5085466 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -34,6 +34,7 @@ import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
import android.os.ParcelableException;
import android.os.RemoteException;
+import android.os.SystemClock;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -179,7 +180,8 @@ public final class AppFunctionManager {
ExecuteAppFunctionAidlRequest aidlRequest =
new ExecuteAppFunctionAidlRequest(
- request, mContext.getUser(), mContext.getPackageName());
+ request, mContext.getUser(), mContext.getPackageName(),
+ /* requestTime= */ SystemClock.elapsedRealtime());
try {
ICancellationSignal cancellationTransport =
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
index e623fa10f474..707d1fc0473e 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
@@ -41,8 +41,9 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
ExecuteAppFunctionRequest.CREATOR.createFromParcel(in);
UserHandle userHandle = UserHandle.CREATOR.createFromParcel(in);
String callingPackage = in.readString8();
+ long requestTime = in.readLong();
return new ExecuteAppFunctionAidlRequest(
- clientRequest, userHandle, callingPackage);
+ clientRequest, userHandle, callingPackage, requestTime);
}
@Override
@@ -60,11 +61,15 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
/** The package name of the app that is requesting to execute the app function. */
private final String mCallingPackage;
- public ExecuteAppFunctionAidlRequest(
- ExecuteAppFunctionRequest clientRequest, UserHandle userHandle, String callingPackage) {
+ /** The time of calling executeAppFunction(). */
+ private final long mRequestTime;
+
+ public ExecuteAppFunctionAidlRequest(ExecuteAppFunctionRequest clientRequest,
+ UserHandle userHandle, String callingPackage, long requestTime) {
this.mClientRequest = Objects.requireNonNull(clientRequest);
this.mUserHandle = Objects.requireNonNull(userHandle);
this.mCallingPackage = Objects.requireNonNull(callingPackage);
+ this.mRequestTime = requestTime;
}
@Override
@@ -77,6 +82,7 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
mClientRequest.writeToParcel(dest, flags);
mUserHandle.writeToParcel(dest, flags);
dest.writeString8(mCallingPackage);
+ dest.writeLong(mRequestTime);
}
/** Returns the client request to execute an app function. */
@@ -96,4 +102,9 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
public String getCallingPackage() {
return mCallingPackage;
}
+
+ /** Returns the time of calling executeAppFunction(). */
+ public long getRequestTime() {
+ return mRequestTime;
+ }
}
diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
index 2426daf5c9f2..e527de209a75 100644
--- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
+++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
@@ -17,11 +17,14 @@
package android.app.appfunctions;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
/**
* A wrapper of IExecuteAppFunctionCallback which swallows the {@link RemoteException}. This
@@ -37,8 +40,19 @@ public class SafeOneTimeExecuteAppFunctionCallback {
@NonNull private final IExecuteAppFunctionCallback mCallback;
+ @Nullable
+ private final CompletionCallback mCompletionCallback;
+
+ private final AtomicLong mExecutionStartTimeAfterBindMillis = new AtomicLong();
+
public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) {
+ this(callback, /* completionCallback= */ null);
+ }
+
+ public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback,
+ @Nullable CompletionCallback completionCallback) {
mCallback = Objects.requireNonNull(callback);
+ mCompletionCallback = completionCallback;
}
/** Invoke wrapped callback with the result. */
@@ -49,6 +63,10 @@ public class SafeOneTimeExecuteAppFunctionCallback {
}
try {
mCallback.onSuccess(result);
+ if (mCompletionCallback != null) {
+ mCompletionCallback.finalizeOnSuccess(result,
+ mExecutionStartTimeAfterBindMillis.get());
+ }
} catch (RemoteException ex) {
// Failed to notify the other end. Ignore.
Log.w(TAG, "Failed to invoke the callback", ex);
@@ -63,6 +81,10 @@ public class SafeOneTimeExecuteAppFunctionCallback {
}
try {
mCallback.onError(error);
+ if (mCompletionCallback != null) {
+ mCompletionCallback.finalizeOnError(error,
+ mExecutionStartTimeAfterBindMillis.get());
+ }
} catch (RemoteException ex) {
// Failed to notify the other end. Ignore.
Log.w(TAG, "Failed to invoke the callback", ex);
@@ -76,4 +98,27 @@ public class SafeOneTimeExecuteAppFunctionCallback {
public void disable() {
mOnResultCalled.set(true);
}
+
+ /**
+ * Sets the execution start time of the request. Used to calculate the overhead latency of
+ * requests.
+ */
+ public void setExecutionStartTimeMillis() {
+ if (!mExecutionStartTimeAfterBindMillis.compareAndSet(0, SystemClock.elapsedRealtime())) {
+ Log.w(TAG, "Ignore subsequent calls to setExecutionStartTimeMillis()");
+ }
+ }
+
+ /**
+ * Provides a hook to execute additional actions after the {@link IExecuteAppFunctionCallback}
+ * has been invoked.
+ */
+ public interface CompletionCallback {
+ /** Called after {@link IExecuteAppFunctionCallback#onSuccess}. */
+ void finalizeOnSuccess(@NonNull ExecuteAppFunctionResponse result,
+ long executionStartTimeMillis);
+
+ /** Called after {@link IExecuteAppFunctionCallback#onError}. */
+ void finalizeOnError(@NonNull AppFunctionException error, long executionStartTimeMillis);
+ }
}
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index 7d21cbf955d9..258ce06bffc3 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -16,8 +16,6 @@
package android.app.compat;
-import static android.app.PropertyInvalidatedCache.createSystemCacheKey;
-
import android.annotation.NonNull;
import android.app.PropertyInvalidatedCache;
import android.content.Context;
@@ -34,7 +32,10 @@ import com.android.internal.compat.IPlatformCompat;
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
- private static final String CACHE_KEY = createSystemCacheKey("is_compat_change_enabled");
+
+ private static final String CACHE_MODULE = PropertyInvalidatedCache.MODULE_SYSTEM;
+ private static final String CACHE_API = "is_compat_change_enabled";
+
private static final int MAX_ENTRIES = 2048;
private static boolean sDisabled = getDefaultDisabled();
private volatile IPlatformCompat mPlatformCompat;
@@ -51,7 +52,12 @@ public final class ChangeIdStateCache
/** @hide */
public ChangeIdStateCache() {
- super(MAX_ENTRIES, CACHE_KEY);
+ super(new PropertyInvalidatedCache.Args(CACHE_MODULE)
+ .maxEntries(MAX_ENTRIES)
+ .isolateUids(false)
+ .cacheNulls(false)
+ .api(CACHE_API),
+ CACHE_API, null);
}
/**
@@ -72,7 +78,7 @@ public final class ChangeIdStateCache
*/
public static void invalidate() {
if (!sDisabled) {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY);
+ PropertyInvalidatedCache.invalidateCache(CACHE_MODULE, CACHE_API);
}
}
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 529b59ac424d..e8cfd79c9cc7 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -17,14 +17,14 @@ flag {
flag {
name: "multi_window_screen_context"
- namespace: "machine_learning"
+ namespace: "sysui_integrations"
description: "Report screen context and positions for all windows."
bug: "371065456"
}
flag {
name: "contextual_search_window_layer"
- namespace: "machine_learning"
+ namespace: "sysui_integrations"
description: "Identify live contextual search UI to exclude from contextual search screenshot."
bug: "372510690"
} \ No newline at end of file
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7ceaeb3fb070..c9472598b352 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -215,7 +215,8 @@ public class JankDataProcessor {
try {
mPendingJankStats.values().forEach(stat -> {
- FrameworkStatsLog.write(FrameworkStatsLog.JANK_FRAME_COUNT_BY_WIDGET,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.JANK_FRAME_COUNT_BY_WIDGET_REPORTED,
/*app uid*/ stat.getUid(),
/*activity name*/ stat.getActivityName(),
/*widget id*/ stat.getWidgetId(),
diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java
index 469521668d25..a04f96a9f6e3 100644
--- a/core/java/android/app/jank/JankTracker.java
+++ b/core/java/android/app/jank/JankTracker.java
@@ -29,6 +29,7 @@ import android.view.ViewTreeObserver;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.HashMap;
/**
* This class is responsible for registering callbacks that will receive JankData batches.
@@ -174,6 +175,15 @@ public class JankTracker {
}
/**
+ * Retrieve all pending jank stats before they are logged, this is intended for testing
+ * purposes only.
+ */
+ @VisibleForTesting
+ public HashMap<String, JankDataProcessor.PendingJankStat> getPendingJankStats() {
+ return mJankDataProcessor.getPendingJankStats();
+ }
+
+ /**
* Only intended to be used by tests, the runnable that registers the listeners may not run
* in time for tests to pass. This forces them to run immediately.
*/
@@ -192,7 +202,11 @@ public class JankTracker {
*/
}
- private boolean shouldTrack() {
+ /**
+ * Returns whether jank tracking is enabled or not.
+ */
+ @VisibleForTesting
+ public boolean shouldTrack() {
return mTrackingEnabled && mListenersRegistered;
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 8b6840c1b552..b1db1379e400 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -284,6 +284,20 @@ flag {
}
flag {
+ name: "nm_binder_perf_cache_channels"
+ namespace: "systemui"
+ description: "Use IpcDataCache for notification channel/group lookups"
+ bug: "362981561"
+}
+
+flag {
+ name: "nm_binder_perf_throttle_notify"
+ namespace: "systemui"
+ description: "Rate-limit calls to enqueueNotificationWithTag client-side"
+ bug: "362981561"
+}
+
+flag {
name: "no_sbnholder"
namespace: "systemui"
description: "removes sbnholder from NLS"
diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS
deleted file mode 100644
index 85e9e653e6fb..000000000000
--- a/core/java/android/app/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 1363385
-
-sandeepbandaru@google.com
-shivanker@google.com
-hackz@google.com
-volnov@google.com
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
deleted file mode 100644
index 74a96c864167..000000000000
--- a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
+++ /dev/null
@@ -1,17 +0,0 @@
-package: "android.app.ondeviceintelligence.flags"
-container: "system"
-
-flag {
- name: "enable_on_device_intelligence"
- is_exported: true
- namespace: "ondeviceintelligence"
- description: "Make methods on OnDeviceIntelligenceManager available for local inference."
- bug: "304755128"
-}
-flag {
- name: "enable_on_device_intelligence_module"
- is_exported: true
- namespace: "ondeviceintelligence"
- description: "Enable migration to mainline module and related changes."
- bug: "376427781"
-} \ No newline at end of file
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 359c84eeb559..238f1cb12208 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -37,6 +37,14 @@ flag {
flag {
namespace: "system_performance"
+ name: "pic_separate_permission_notifications"
+ is_fixed_read_only: true
+ description: "Seperate PermissionManager notifications from cache udpates"
+ bug: "379699402"
+}
+
+flag {
+ namespace: "system_performance"
name: "pic_cache_nulls"
is_fixed_read_only: true
description: "Cache null returns from binder calls"
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 2b52681d1be8..75ecabd8ddb0 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -56,7 +56,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(getActivityToken(), mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index cecf7013c79c..bb881908d10f 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -89,7 +89,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
public void preExecute(@NonNull ClientTransactionHandler client) {
// The local config is already scaled so only apply if this item is from server side.
if (!client.isExecutingLocalTransaction()) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfig);
}
mActivityClientRecord = client.prepareRelaunchActivity(getActivityToken(), mPendingResults,
mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow, mActivityWindowInfo);
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 123d7926160c..e42005bdd595 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -46,7 +46,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem {
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
client.updatePendingConfiguration(mConfiguration);
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 235a9f7aeb4c..f2e7a4fcd50b 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -202,8 +202,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
public void preExecute(@NonNull ClientTransactionHandler client) {
client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mOverrideConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mCurConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mOverrideConfig);
client.updatePendingConfiguration(mCurConfig);
if (mActivityClientController != null) {
ActivityClient.setActivityClientController(mActivityClientController);
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 1aa563aa6363..72d1f491f2c6 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -58,7 +58,7 @@ public class MoveToDisplayItem extends ActivityTransactionItem {
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(getActivityToken(), mConfiguration);
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index aee1cd9b4760..a5b58f968c27 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,8 +16,10 @@
package android.app.supervision;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.RemoteException;
@@ -32,9 +34,7 @@ public class SupervisionManager {
private final Context mContext;
private final ISupervisionManager mService;
- /**
- * @hide
- */
+ /** @hide */
@UnsupportedAppUsage
public SupervisionManager(Context context, ISupervisionManager service) {
mContext = context;
@@ -48,8 +48,23 @@ public class SupervisionManager {
*/
@UserHandleAware
public boolean isSupervisionEnabled() {
+ return isSupervisionEnabledForUser(mContext.getUserId());
+ }
+
+ /**
+ * Returns whether the device is supervised.
+ *
+ * <p>The caller must be from the same user as the target or hold the {@link
+ * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @hide
+ */
+ @RequiresPermission(
+ value = android.Manifest.permission.INTERACT_ACROSS_USERS,
+ conditional = true)
+ public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
try {
- return mService.isSupervisionEnabledForUser(mContext.getUserId());
+ return mService.isSupervisionEnabledForUser(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/supervision/SupervisionManagerInternal.java b/core/java/android/app/supervision/SupervisionManagerInternal.java
index d571e14ff5fa..2cf6ae6f9d01 100644
--- a/core/java/android/app/supervision/SupervisionManagerInternal.java
+++ b/core/java/android/app/supervision/SupervisionManagerInternal.java
@@ -27,32 +27,41 @@ import android.os.PersistableBundle;
*/
public abstract class SupervisionManagerInternal {
/**
- * Returns whether supervision is enabled for the specified user
+ * Returns whether the app with given process uid is the active supervision app.
*
- * @param userId The user to retrieve the supervision state for
- * @return whether the user is supervised
+ * <p>Supervision app is considered active when supervision is enabled for the user running the
+ * given process uid.
+ *
+ * @param uid App process uid.
+ * @return Whether the app is the active supervision app.
*/
- public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
+ public abstract boolean isActiveSupervisionApp(int uid);
/**
- * Returns whether the supervision lock screen needs to be shown.
+ * Returns whether supervision is enabled for the specified user.
+ *
+ * @param userId The user to retrieve the supervision state for.
+ * @return Whether the user is supervised.
*/
+ public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
+
+ /** Returns whether the supervision lock screen needs to be shown. */
public abstract boolean isSupervisionLockscreenEnabledForUser(@UserIdInt int userId);
/**
* Set whether supervision is enabled for the specified user.
*
- * @param userId The user to set the supervision state for
- * @param enabled Whether or not the user should be supervised
+ * @param userId The user to set the supervision state for.
+ * @param enabled Whether or not the user should be supervised.
*/
public abstract void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled);
/**
- * Sets whether the supervision lock screen should be shown for the specified user
+ * Sets whether the supervision lock screen should be shown for the specified user.
*
- * @param userId The user set the superivision state for
- * @param enabled Whether or not the superivision lock screen needs to be shown
- * @param options Optional configuration parameters for the supervision lock screen
+ * @param userId The user set the superivision state for.
+ * @param enabled Whether or not the superivision lock screen needs to be shown.
+ * @param options Optional configuration parameters for the supervision lock screen.
*/
public abstract void setSupervisionLockscreenEnabledForUser(
@UserIdInt int userId, boolean enabled, @Nullable PersistableBundle options);
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index d4f82f665fd4..1b0353274fb9 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -24,3 +24,11 @@ flag {
description: "Flag that enables supervision when the supervision app is the profile owner"
bug: "377261590"
}
+
+flag {
+ name: "deprecate_dpm_supervision_apis"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that deprecates supervision methods in DPM"
+ bug: "382034839"
+}
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index ca2d9e676a02..999a5da870ad 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -118,14 +118,16 @@ public final class WallpaperDescription implements Parcelable {
}
/**
- * @return the title for this wallpaper, with each list element intended to be a separate
- * line, or {@code null} if not provided
+ * @return the title for this wallpaper, or {@code null} if not provided
*/
@Nullable public CharSequence getTitle() {
return mTitle;
}
- /** @return the description for this wallpaper */
+ /**
+ * @return the description for this wallpaper, with each list element intended to be shown on a
+ * separate line in the UI
+ */
@NonNull
public List<CharSequence> getDescription() {
return mDescription;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 40de2985f68a..67ad4594599f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -37,36 +37,44 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.Intent.FilterComparison;
import android.content.IntentSender;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FunctionalUtils;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -592,6 +600,8 @@ public class AppWidgetManager {
private boolean mHasPostedLegacyLists = false;
+ private @NonNull ServiceCollectionCache mServiceCollectionCache;
+
/**
* Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
* Context} object.
@@ -612,6 +622,7 @@ public class AppWidgetManager {
mPackageName = context.getOpPackageName();
mService = service;
mDisplayMetrics = context.getResources().getDisplayMetrics();
+ mServiceCollectionCache = new ServiceCollectionCache(context, /* timeout= */ 5000L);
if (mService == null) {
return;
}
@@ -649,7 +660,7 @@ public class AppWidgetManager {
final RemoteViews viewsCopy = new RemoteViews(original);
Runnable updateWidgetWithTask = () -> {
try {
- viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
+ viewsCopy.collectAllIntents(mMaxBitmapMemory, mServiceCollectionCache).get();
action.acceptOrThrow(viewsCopy);
} catch (Exception e) {
Log.e(TAG, failureMsg, e);
@@ -1629,4 +1640,106 @@ public class AppWidgetManager {
thread.start();
return thread.getThreadHandler();
}
+
+ /**
+ * @hide
+ */
+ public static class ServiceCollectionCache {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final long mTimeOut;
+
+ private final Map<FilterComparison, ConnectionTask> mActiveConnections =
+ new ArrayMap<>();
+
+ public ServiceCollectionCache(Context context, long timeOut) {
+ mContext = context;
+ mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+ mTimeOut = timeOut;
+ }
+
+ /**
+ * Connect to the service indicated by the {@code Intent}, and consume the binder on the
+ * specified executor
+ */
+ public void connectAndConsume(Intent intent, Consumer<IBinder> task, Executor executor) {
+ mHandler.post(() -> connectAndConsumeInner(intent, task, executor));
+ }
+
+ private void connectAndConsumeInner(Intent intent, Consumer<IBinder> task,
+ Executor executor) {
+ ConnectionTask activeConnection = mActiveConnections.computeIfAbsent(
+ new FilterComparison(intent), ConnectionTask::new);
+ activeConnection.add(task, executor);
+ }
+
+ private class ConnectionTask implements ServiceConnection {
+
+ private final Runnable mDestroyAfterTimeout = this::onDestroyTimeout;
+ private final ArrayDeque<Pair<Consumer<IBinder>, Executor>> mTaskQueue =
+ new ArrayDeque<>();
+
+ private boolean mOnDestroyTimeout = false;
+ private IBinder mIBinder;
+
+ ConnectionTask(@NonNull FilterComparison filter) {
+ mContext.bindService(filter.getIntent(),
+ Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
+ mHandler::post,
+ this);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ mIBinder = iBinder;
+ mHandler.post(this::handleNext);
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ // Use an empty binder, follow up tasks will handle the failure
+ onServiceConnected(name, new Binder());
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+
+ void add(Consumer<IBinder> task, Executor executor) {
+ mTaskQueue.add(Pair.create(task, executor));
+ if (mOnDestroyTimeout) {
+ // If we are waiting for timeout, cancel it and execute the next task
+ handleNext();
+ }
+ }
+
+ private void handleNext() {
+ mHandler.removeCallbacks(mDestroyAfterTimeout);
+ Pair<Consumer<IBinder>, Executor> next = mTaskQueue.pollFirst();
+ if (next != null) {
+ mOnDestroyTimeout = false;
+ next.second.execute(() -> {
+ next.first.accept(mIBinder);
+ mHandler.post(this::handleNext);
+ });
+ } else {
+ // Finished all tasks, start a timeout to unbind this service
+ mOnDestroyTimeout = true;
+ mHandler.postDelayed(mDestroyAfterTimeout, mTimeOut);
+ }
+ }
+
+ /**
+ * Called after we have waited for {@link #mTimeOut} after the last task is finished
+ */
+ private void onDestroyTimeout() {
+ if (!mTaskQueue.isEmpty()) {
+ handleNext();
+ return;
+ }
+ mContext.unbindService(this);
+ mActiveConnections.values().remove(this);
+ }
+ }
+ }
}
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index f368935a74c8..32cbf326c923 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -90,9 +90,9 @@ public final class AssociationRequest implements Parcelable {
public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";
/**
- * Device profile: a virtual display capable of rendering Android applications, and sending back
+ * Device profile: a virtual device capable of rendering Android applications, and sending back
* input events.
- *
+ * <p>
* Only applications that have been granted
* {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING} are allowed to
* request to be associated with such devices.
@@ -106,7 +106,7 @@ public final class AssociationRequest implements Parcelable {
/**
* Device profile: a virtual device capable of rendering content from an Android host to a
* nearby device.
- *
+ * <p>
* Only applications that have been granted
* {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING}
* are allowed to request to be associated with such devices.
@@ -118,6 +118,21 @@ public final class AssociationRequest implements Parcelable {
"android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING";
/**
+ * Device profile: a virtual device capable of streaming sensor data such as camera, audio and
+ * IMU between an Android host and a nearby device.
+ * <p>
+ * Only applications that have been granted
+ * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING}
+ * are allowed to request to be associated with such devices.
+ *
+ * @see AssociationRequest.Builder#setDeviceProfile
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+ @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING)
+ public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING =
+ "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
+
+ /**
* Device profile: Android Automotive Projection
*
* Only applications that have been granted
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
index f66a1ae5c175..2f19eb49ad43 100644
--- a/core/java/android/companion/DeviceId.java
+++ b/core/java/android/companion/DeviceId.java
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
-import android.provider.OneTimeUseBuilder;
import java.util.Locale;
import java.util.Objects;
@@ -154,8 +153,12 @@ public final class DeviceId implements Parcelable {
/**
* A builder for {@link DeviceId}
+ *
+ * <p>Calling apps must provide at least one of the following to identify
+ * the device: a custom ID using {@link #setCustomId(String)}, or a MAC address using
+ * {@link #setMacAddress(MacAddress)}.</p>
*/
- public static final class Builder extends OneTimeUseBuilder<DeviceId> {
+ public static final class Builder {
private String mCustomId;
private MacAddress mMacAddress;
@@ -171,7 +174,6 @@ public final class DeviceId implements Parcelable {
*/
@NonNull
public Builder setCustomId(@Nullable String customId) {
- checkNotUsed();
if (customId != null
&& customId.length() > CUSTOM_ID_LENGTH_LIMIT) {
throw new IllegalArgumentException("Length of the custom id must be at most "
@@ -191,15 +193,12 @@ public final class DeviceId implements Parcelable {
*/
@NonNull
public Builder setMacAddress(@Nullable MacAddress macAddress) {
- checkNotUsed();
mMacAddress = macAddress;
return this;
}
@NonNull
- @Override
public DeviceId build() {
- markUsed();
if (mCustomId == null && mMacAddress == null) {
throw new IllegalArgumentException("At least one device id property must be"
+ "non-null to build a DeviceId.");
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index b3f09a98d623..ed2fd99c55c5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1290,7 +1290,11 @@ public final class VirtualDeviceManager {
@NonNull UserHandle user) {}
/**
- * Called when a window with a secure surface is no longer shown on the device.
+ * Called when there is no longer any window with a secure surface shown on the device.
+ *
+ * <p>This is only called once there are no more secure windows shown on the device. If
+ * there are multiple secure windows shown on the device, this callback will be called only
+ * once all of them are hidden.</p>
*
* @param displayId The display ID on which the window was shown before.
*
diff --git a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
index 5a1325519699..78f8ab46f989 100644
--- a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
+++ b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
@@ -64,4 +64,9 @@ interface IVirtualDeviceManagerNative {
* Returns the device policy for the given virtual device and policy type.
*/
int getDevicePolicy(int deviceId, int policyType);
+
+ /**
+ * Returns the ID of the device which owns the display with the given ID.
+ */
+ int getDeviceIdForDisplayId(int displayId);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ec6a62ff639..7abf5600d659 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -788,6 +788,40 @@ public abstract class Context {
public static final int RECEIVER_NOT_EXPORTED = 0x4;
/**
+ * The permission is granted.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_GRANTED = 0;
+
+ /**
+ * The permission isn't granted, but apps can request the permission. When the app request
+ * the permission, user will be prompted with permission dialog to grant or deny the request.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_REQUESTABLE = 1;
+
+ /**
+ * The permission is denied, and shouldn't be requested by apps. Permission request
+ * will be automatically denied by the system, preventing the permission dialog from being
+ * displayed to the user.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_UNREQUESTABLE = 2;
+
+
+ /** @hide */
+ @IntDef(prefix = { "PERMISSION_REQUEST_STATE_" }, value = {
+ PERMISSION_REQUEST_STATE_GRANTED,
+ PERMISSION_REQUEST_STATE_REQUESTABLE,
+ PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionRequestState {}
+
+ /**
* Returns an AssetManager instance for the application's package.
* <p>
* <strong>Note:</strong> Implementations of this method should return
@@ -6989,6 +7023,31 @@ public abstract class Context {
@NonNull @PermissionName String permission, @Nullable String message);
/**
+ * Returns the permission request state for a given runtime permission. This method provides a
+ * streamlined mechanism for applications to determine whether a permission can be
+ * requested (i.e. whether the user will be prompted with a permission dialog).
+ *
+ * <p>Traditionally, determining if a permission has been permanently denied (unrequestable)
+ * required applications to initiate a permission request and subsequently analyze the result
+ * of {@link android.app.Activity#shouldShowRequestPermissionRationale} in conjunction with the
+ * grant result within the {@link android.app.Activity#onRequestPermissionsResult} callback.
+ *
+ * @param permission The name of the permission.
+ *
+ * @return The current request state of the specified permission, represented by one of the
+ * following constants: {@link PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+ * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+ * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}.
+ *
+ * @hide
+ */
+ @CheckResult
+ @PermissionRequestState
+ public int getPermissionRequestState(@NonNull String permission) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Grant permission to access a specific Uri to another package, regardless
* of whether that package has general permission to access the Uri's
* content provider. This can be used to grant specific, temporary
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 413eb9886392..a146807b9fcf 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1012,6 +1012,12 @@ public class ContextWrapper extends Context {
mBase.enforceCallingOrSelfPermission(permission, message);
}
+ /** @hide */
+ @Override
+ public int getPermissionRequestState(String permission) {
+ return mBase.getPermissionRequestState(permission);
+ }
+
@Override
public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
mBase.grantUriPermission(toPackage, uri, modeFlags);
diff --git a/core/java/android/content/EventLogTags.logtags b/core/java/android/content/EventLogTags.logtags
index 21ea90ad2e1e..861a5b72c86c 100644
--- a/core/java/android/content/EventLogTags.logtags
+++ b/core/java/android/content/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.content;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a577ec0d4749..3d75423edfa9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
import android.os.BundleMerger;
@@ -7857,6 +7858,10 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int URI_ALLOW_UNSAFE = 1<<2;
+ static {
+ Bundle.intentClass = Intent.class;
+ }
+
// ---------------------------------------------------------------------
private String mAction;
@@ -12286,7 +12291,6 @@ public class Intent implements Parcelable, Cloneable {
private IBinder mCreatorToken;
// Stores all extra keys whose values are intents for a top level intent.
private ArraySet<NestedIntentKey> mNestedIntentKeys;
-
}
/**
@@ -12348,6 +12352,7 @@ public class Intent implements Parcelable, Cloneable {
public int hashCode() {
return Objects.hash(mType, mKey, mIndex);
}
+
}
private @Nullable CreatorTokenInfo mCreatorTokenInfo;
@@ -12398,9 +12403,20 @@ public class Intent implements Parcelable, Cloneable {
addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
- Object value = mExtras.get(key);
-
- if (value instanceof Intent intent && !visited.contains(intent)) {
+ Object value;
+ try {
+ value = mExtras.get(key);
+ } catch (BadParcelableException e) {
+ // This could happen when the key points to a LazyValue whose class cannot be
+ // found by the classLoader - A nested object more than 1 level deeper who is
+ // of type of a custom class could trigger this situation. In such case, we
+ // ignore it since it is not an intent. However, it could be a custom type that
+ // extends from Intent. If such an object is retrieved later in another
+ // component, then trying to launch such a custom class object will fail unless
+ // removeLaunchSecurityProtection() is called before it is launched.
+ value = null;
+ }
+ if (value instanceof Intent intent) {
handleNestedIntent(intent, visited, new NestedIntentKey(
NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
} else if (value instanceof Parcelable[] parcelables) {
@@ -12423,7 +12439,6 @@ public class Intent implements Parcelable, Cloneable {
}
private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) {
- visited.add(intent);
if (mCreatorTokenInfo == null) {
mCreatorTokenInfo = new CreatorTokenInfo();
}
@@ -12431,7 +12446,10 @@ public class Intent implements Parcelable, Cloneable {
mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>();
}
mCreatorTokenInfo.mNestedIntentKeys.add(key);
- intent.collectNestedIntentKeysRecur(visited);
+ if (!visited.contains(intent)) {
+ visited.add(intent);
+ intent.collectNestedIntentKeysRecur(visited);
+ }
}
private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a06eb1c5b4ad..438a21b7942f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3151,16 +3151,6 @@ public abstract class PackageManager {
public static final long MAXIMUM_VERIFICATION_TIMEOUT = 60*60*1000;
/**
- * As the generated feature count is useful for classes that may not be compiled in the same
- * annotation processing unit as PackageManager, we redeclare it here for visibility.
- *
- * @hide
- */
- @VisibleForTesting
- public static final int SDK_FEATURE_COUNT =
- com.android.internal.pm.SystemFeaturesMetadata.SDK_FEATURE_COUNT;
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
* audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
* lag in sound input or output.
@@ -11651,7 +11641,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
sApplicationInfoCache =
new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
- 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
"getApplicationInfo") {
@Override
public ApplicationInfo recompute(ApplicationInfoQuery query) {
@@ -11682,18 +11672,6 @@ public abstract class PackageManager {
sApplicationInfoCache.disableLocal();
}
- private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
- new PropertyInvalidatedCache.AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO);
-
- /**
- * Invalidate caches of package and permission information system-wide.
- *
- * @hide
- */
- public static void invalidatePackageInfoCache() {
- sCacheAutoCorker.autoCork();
- }
-
// Some of the flags don't affect the query result, but let's be conservative and cache
// each combination of flags separately.
@@ -11752,7 +11730,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
"getPackageInfo") {
@Override
public PackageInfo recompute(PackageInfoQuery query) {
@@ -11784,17 +11762,40 @@ public abstract class PackageManager {
/**
* Inhibit package info cache invalidations when correct.
*
- * @hide */
+ * @hide
+ */
public static void corkPackageInfoCache() {
- PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+ sPackageInfoCache.corkInvalidations();
}
/**
* Enable package info cache invalidations.
*
- * @hide */
+ * @hide
+ */
public static void uncorkPackageInfoCache() {
- PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+ sPackageInfoCache.uncorkInvalidations();
+ }
+
+ // This auto-corker is obsolete once the separate permission notifications feature is
+ // committed.
+ private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
+ PropertyInvalidatedCache.separatePermissionNotificationsEnabled()
+ ? null
+ : new PropertyInvalidatedCache
+ .AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+
+ /**
+ * Invalidate caches of package and permission information system-wide.
+ *
+ * @hide
+ */
+ public static void invalidatePackageInfoCache() {
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ sPackageInfoCache.invalidateCache();
+ } else {
+ sCacheAutoCorker.autoCork();
+ }
}
/**
@@ -12012,11 +12013,8 @@ public abstract class PackageManager {
* file.
*
* @throws SigningInfoException if the verification fails
- *
- * @hide
*/
@FlaggedApi(android.content.pm.Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static @NonNull SigningInfo getVerifiedSigningInfo(@NonNull String path,
@AppSigningSchemeVersion int minAppSigningSchemeVersion) throws SigningInfoException {
ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
@@ -12028,4 +12026,28 @@ public abstract class PackageManager {
}
return new SigningInfo(result.getResult());
}
+
+ /**
+ * As the generated feature count is useful for classes that may not be compiled in the same
+ * annotation processing unit as PackageManager, we redeclare it here for visibility.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int SDK_FEATURE_COUNT =
+ com.android.internal.pm.SystemFeaturesMetadata.SDK_FEATURE_COUNT;
+
+ /**
+ * Returns a stable index for PackageManager-defined features.
+ *
+ * <p> Similar to {@link #SDK_FEATURE_COUNT}, we redeclare this utility method generated by the
+ * annotation processor for internal visibility.
+ *
+ * @return index in [0, {@link #SDK_FEATURECOUNT}) for PackageManager-defined features, else -1.
+ * @hide
+ */
+ @VisibleForTesting
+ public static int maybeGetSdkFeatureIndex(String featureName) {
+ return com.android.internal.pm.SystemFeaturesMetadata.maybeGetSdkFeatureIndex(featureName);
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7db9f8..219b20428d7a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2322,10 +2322,10 @@ public class PackageParser {
} else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
sa = res.obtainAttributes(parser,
- com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+ com.android.internal.R.styleable.AndroidManifestAdoptPermissions);
String name = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
+ com.android.internal.R.styleable.AndroidManifestAdoptPermissions_name, 0);
sa.recycle();
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index e4fbd1f28dbb..21bbb0a0a81c 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -22,7 +22,6 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,41 +39,29 @@ public final class SigningInfo implements Parcelable {
/**
* JAR signing (v1 scheme).
* See https://source.android.com/docs/security/features/apksigning#v1.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int VERSION_JAR = SignatureSchemeVersion.JAR;
/**
* APK signature scheme v2.
* See https://source.android.com/docs/security/features/apksigning/v2.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int VERSION_SIGNING_BLOCK_V2 = SignatureSchemeVersion.SIGNING_BLOCK_V2;
/**
* APK signature scheme v3.
* See https://source.android.com/docs/security/features/apksigning/v3.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int VERSION_SIGNING_BLOCK_V3 = SignatureSchemeVersion.SIGNING_BLOCK_V3;
/**
* APK signature scheme v4.
* See https://source.android.com/docs/security/features/apksigning/v4.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int VERSION_SIGNING_BLOCK_V4 = SignatureSchemeVersion.SIGNING_BLOCK_V4;
/** @hide */
@@ -255,11 +242,8 @@ public final class SigningInfo implements Parcelable {
/**
* Returns true if the signing certificates in this and other match exactly.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean signersMatchExactly(@NonNull SigningInfo other) {
return mSigningDetails.signaturesMatchExactly(other.mSigningDetails);
}
diff --git a/core/java/android/content/pm/SigningInfoException.java b/core/java/android/content/pm/SigningInfoException.java
index a81e07e73685..2fd1bfb46f4c 100644
--- a/core/java/android/content/pm/SigningInfoException.java
+++ b/core/java/android/content/pm/SigningInfoException.java
@@ -19,17 +19,13 @@ package android.content.pm;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
/**
* Indicates an error when verifying the
* <a href="https://source.android.com/docs/security/features/apksigning">app signing</a>
* information.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public class SigningInfoException extends Exception {
private final int mCode;
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 44f2a4ca38e2..23f1ff8926df 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -158,6 +158,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"options":[
{
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index dfeee2a2335f..00ddae334ef2 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -362,3 +362,9 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "remove_hidden_module_usage"
+ namespace: "modularization"
+ description: "Feature flag to remove the consumption of the hidden module status (ModuleInfo#IsHidden) in the Android source tree."
+ bug: "363952383"
+}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 18a45d8d442e..53813012b4b3 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -539,9 +539,6 @@ public class ApkLiteParseUtils {
hasBindDeviceAdminPermission);
break;
case TAG_USES_SDK_LIBRARY:
- if (!android.content.pm.Flags.sdkDependencyInstaller()) {
- break;
- }
String usesSdkLibName = parser.getAttributeValue(
ANDROID_RES_NAMESPACE, "name");
// TODO(b/379219371): Due to a bug in bundletool, some apps can use
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index d6620d19ccf0..afcdcb0fbcad 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -17,7 +17,9 @@
package android.content.res;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.graphics.Canvas;
import android.graphics.Insets;
@@ -34,14 +36,17 @@ import android.util.MergedConfiguration;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import com.android.aconfig.annotations.VisibleForTesting;
+
/**
* CompatibilityInfo class keeps the information about the screen compatibility mode that the
* application is running under.
- *
- * {@hide}
+ *
+ * {@hide}
*/
@RavenwoodKeepWholeClass
public class CompatibilityInfo implements Parcelable {
@@ -129,12 +134,37 @@ public class CompatibilityInfo implements Parcelable {
*/
public final float applicationDensityInvertedScale;
+ /**
+ * Application's display rotation.
+ *
+ * <p>This field is used to sandbox fixed-orientation activities on displays or display areas
+ * with ignoreOrientationRequest, where the display orientation is more likely to be different
+ * from the orientation the activity requested (e.g. in desktop windowing, or letterboxed).
+ * Mainly set for activities which use the display rotation to orient their content, for example
+ * camera previews.
+ *
+ * <p>In the case of camera activities, assuming the wrong posture
+ * can lead to sideways or stretched previews. As part of camera compat treatment for desktop
+ * windowing, the app is sandboxed to believe that the app and the device are in the posture the
+ * app requested. For example for portrait fixed-orientation apps, the app is letterboxed to
+ * portrait, camera feed is cropped to portrait, and the display rotation is changed via this
+ * field, for example to {@link Surface.Rotation#ROTATION_0} on devices with portrait natural
+ * orientation. All of these parameters factor in common calculations for setting up the camera
+ * preview.
+ */
+ @Surface.Rotation
+ public int applicationDisplayRotation = WindowConfiguration.ROTATION_UNDEFINED;
+
/** The process level override inverted scale. See {@link #HAS_OVERRIDE_SCALING}. */
private static float sOverrideInvertedScale = 1f;
/** The process level override inverted density scale. See {@link #HAS_OVERRIDE_SCALING}. */
private static float sOverrideDensityInvertScale = 1f;
+ /** The process level override display rotation. */
+ @Surface.Rotation
+ private static int sOverrideDisplayRotation = WindowConfiguration.ROTATION_UNDEFINED;
+
@UnsupportedAppUsage
@Deprecated
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
@@ -346,11 +376,16 @@ public class CompatibilityInfo implements Parcelable {
return (mCompatibilityFlags & HAS_OVERRIDE_SCALING) != 0;
}
+ /** Returns {@code true} if {@link #sOverrideDisplayRotation} should be set. */
+ public boolean isOverrideDisplayRotationRequired() {
+ return applicationDisplayRotation != WindowConfiguration.ROTATION_UNDEFINED;
+ }
+
@UnsupportedAppUsage
public boolean supportsScreen() {
return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
}
-
+
public boolean neverSupportsScreen() {
return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
}
@@ -618,6 +653,9 @@ public class CompatibilityInfo implements Parcelable {
}
public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation, inoutConfig);
+ }
if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
@@ -650,21 +688,42 @@ public class CompatibilityInfo implements Parcelable {
inoutConfig.windowConfiguration.scale(invertScale);
}
- /** @see #sOverrideInvertedScale */
- public static void applyOverrideScaleIfNeeded(Configuration config) {
- if (!hasOverrideScale()) return;
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, config);
+ /** Changes the WindowConfiguration display rotation for the given configuration. */
+ public static void applyDisplayRotationConfiguration(@Surface.Rotation int displayRotation,
+ Configuration inoutConfig) {
+ if (displayRotation != WindowConfiguration.ROTATION_UNDEFINED) {
+ inoutConfig.windowConfiguration.setDisplayRotation(displayRotation);
+ }
}
- /** @see #sOverrideInvertedScale */
- public static void applyOverrideScaleIfNeeded(MergedConfiguration mergedConfig) {
- if (!hasOverrideScale()) return;
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getGlobalConfiguration());
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getOverrideConfiguration());
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getMergedConfiguration());
+ /** @see #sOverrideInvertedScale and #sOverrideDisplayRotation. */
+ public static void applyOverrideIfNeeded(Configuration config) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation, config);
+ }
+ if (hasOverrideScale()) {
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, config);
+ }
+ }
+
+ /** @see #sOverrideInvertedScale and #sOverrideDisplayRotation. */
+ public static void applyOverrideIfNeeded(MergedConfiguration mergedConfig) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getGlobalConfiguration());
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getOverrideConfiguration());
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getMergedConfiguration());
+ }
+ if (hasOverrideScale()) {
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getGlobalConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getOverrideConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getMergedConfiguration());
+ }
}
/** Returns {@code true} if this process is in a environment with override scale. */
@@ -693,6 +752,22 @@ public class CompatibilityInfo implements Parcelable {
return sOverrideDensityInvertScale;
}
+ /** Returns {@code true} if this process is in a environment with override display rotation. */
+ private static boolean hasOverrideDisplayRotation() {
+ return sOverrideDisplayRotation != WindowConfiguration.ROTATION_UNDEFINED;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void setOverrideDisplayRotation(@Surface.Rotation int displayRotation) {
+ sOverrideDisplayRotation = displayRotation;
+ }
+
+ /** @see #sOverrideDisplayRotation */
+ @VisibleForTesting
+ public static int getOverrideDisplayRotation() {
+ return sOverrideDisplayRotation;
+ }
+
/**
* Compute the frame Rect for applications runs under compatibility mode.
*
@@ -747,18 +822,50 @@ public class CompatibilityInfo implements Parcelable {
if (this == o) {
return true;
}
- try {
- CompatibilityInfo oc = (CompatibilityInfo)o;
- if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
- if (applicationDensity != oc.applicationDensity) return false;
- if (applicationScale != oc.applicationScale) return false;
- if (applicationInvertedScale != oc.applicationInvertedScale) return false;
- if (applicationDensityScale != oc.applicationDensityScale) return false;
- if (applicationDensityInvertedScale != oc.applicationDensityInvertedScale) return false;
- return true;
- } catch (ClassCastException e) {
+
+ if (!(o instanceof CompatibilityInfo oc)) {
return false;
}
+
+ if (!isCompatibilityFlagsEqual(oc)) return false;
+ if (!isScaleEqual(oc)) return false;
+ if (!isDisplayRotationEqual(oc)) return false;
+ return true;
+ }
+
+ /**
+ * Checks the difference between this and given {@link CompatibilityInfo} o, and returns the
+ * combination of {@link ActivityInfo}.CONFIG_* changes that this difference should trigger.
+ */
+ public int getCompatibilityChangesForConfig(@Nullable CompatibilityInfo o) {
+ int changes = 0;
+ if (!isDisplayRotationEqual(o)) {
+ changes |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ }
+ if (!isScaleEqual(o) || !isCompatibilityFlagsEqual(o)) {
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+ return changes;
+ }
+
+ private boolean isScaleEqual(@Nullable CompatibilityInfo oc) {
+ if (oc == null) return false;
+ if (applicationDensity != oc.applicationDensity) return false;
+ if (applicationScale != oc.applicationScale) return false;
+ if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+ if (applicationDensityScale != oc.applicationDensityScale) return false;
+ if (applicationDensityInvertedScale != oc.applicationDensityInvertedScale) return false;
+ return true;
+ }
+
+ private boolean isDisplayRotationEqual(@Nullable CompatibilityInfo oc) {
+ return oc != null && oc.applicationDisplayRotation == applicationDisplayRotation;
+ }
+
+ private boolean isCompatibilityFlagsEqual(@Nullable CompatibilityInfo oc) {
+ return oc != null && oc.mCompatibilityFlags == mCompatibilityFlags;
}
@Override
@@ -778,6 +885,10 @@ public class CompatibilityInfo implements Parcelable {
sb.append(" overrideDensityInvScale=");
sb.append(applicationDensityInvertedScale);
}
+ if (isOverrideDisplayRotationRequired()) {
+ sb.append(" overrideDisplayRotation=");
+ sb.append(applicationDisplayRotation);
+ }
if (!supportsScreen()) {
sb.append(" resizing");
}
@@ -800,6 +911,7 @@ public class CompatibilityInfo implements Parcelable {
result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
result = 31 * result + Float.floatToIntBits(applicationDensityScale);
result = 31 * result + Float.floatToIntBits(applicationDensityInvertedScale);
+ result = 31 * result + applicationDisplayRotation;
return result;
}
@@ -816,6 +928,7 @@ public class CompatibilityInfo implements Parcelable {
dest.writeFloat(applicationInvertedScale);
dest.writeFloat(applicationDensityScale);
dest.writeFloat(applicationDensityInvertedScale);
+ dest.writeInt(applicationDisplayRotation);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -839,6 +952,7 @@ public class CompatibilityInfo implements Parcelable {
applicationInvertedScale = source.readFloat();
applicationDensityScale = source.readFloat();
applicationDensityInvertedScale = source.readFloat();
+ applicationDisplayRotation = source.readInt();
}
/**
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 6fc7d90a8237..ecb4bb1394b6 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -114,3 +114,11 @@ flag {
bug: "373535266"
is_fixed_read_only: true
}
+
+flag {
+ name: "self_targeting_android_resource_frro"
+ is_exported: true
+ namespace: "customization_picker"
+ description: "Fixes bug in Launcher preview by enabling overlays targeting 'android'"
+ bug: "377545987"
+} \ No newline at end of file
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
index 830b7e0fa2d0..7eba1819d148 100644
--- a/core/java/android/content/res/loader/ResourcesProvider.java
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -25,6 +25,7 @@ import android.content.om.OverlayManager;
import android.content.pm.ApplicationInfo;
import android.content.res.ApkAssets;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Flags;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -90,6 +91,10 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
throws IOException {
Objects.requireNonNull(overlayInfo);
Preconditions.checkArgument(overlayInfo.isFabricated(), "Not accepted overlay");
+ if (!Flags.selfTargetingAndroidResourceFrro()) {
+ Preconditions.checkStringNotEmpty(
+ overlayInfo.getTargetOverlayableName(), "Without overlayable name");
+ }
final String overlayName =
OverlayManagerImpl.checkOverlayNameValid(overlayInfo.getOverlayName());
final String path =
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 6c35d106bfb7..9c811fb84da3 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -87,6 +87,16 @@ flag {
flag {
namespace: "credential_manager"
+ name: "framework_session_id_metric_bundle"
+ description: "Enables the session_id to be passed across to the UI logs"
+ bug: "379880133"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "credential_manager"
name: "clear_credentials_fix_enabled"
description: "Fixes bug in clearCredential API that causes indefinite suspension"
bug: "314926460"
@@ -104,3 +114,23 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "credential_manager"
+ name: "propagate_user_context_for_intent_creation"
+ description: "Propagates the user ID in which to find the right OEM UI component to launch"
+ bug: "373711451"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "settings_w_fixes"
+ description: "Settings improvements for credential manager"
+ bug: "373711451"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index c521b96fd8ee..59539c40d636 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -16,7 +16,7 @@
package android.credentials.selection;
-import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED;
+import static android.credentials.flags.Flags.FLAG_PROPAGATE_USER_CONTEXT_FOR_INTENT_CREATION;
import static android.credentials.flags.Flags.configurableSelectorUiEnabled;
import android.annotation.FlaggedApi;
@@ -24,6 +24,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.text.TextUtils;
import android.util.Slog;
@@ -46,7 +49,7 @@ import java.util.ArrayList;
* @hide
*/
@TestApi
-@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED)
+@FlaggedApi(FLAG_PROPAGATE_USER_CONTEXT_FOR_INTENT_CREATION)
public class IntentFactory {
/**
@@ -65,9 +68,10 @@ public class IntentFactory {
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver) {
+ @NonNull ResultReceiver resultReceiver,
+ @UserIdInt int userId) {
return createCredentialSelectorIntentInternal(context, requestInfo,
- disabledProviderDataList, resultReceiver);
+ disabledProviderDataList, resultReceiver, userId);
}
/**
@@ -96,9 +100,10 @@ public class IntentFactory {
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver) {
+ @NonNull ResultReceiver resultReceiver,
+ @UserIdInt int userId) {
IntentCreationResult result = createCredentialSelectorIntentInternal(context, requestInfo,
- disabledProviderDataList, resultReceiver);
+ disabledProviderDataList, resultReceiver, userId);
result.getIntent().putParcelableArrayListExtra(
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
return result;
@@ -130,9 +135,10 @@ public class IntentFactory {
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver) {
+ @NonNull ResultReceiver resultReceiver, @UserIdInt int userId) {
return createCredentialSelectorIntentForCredMan(context, requestInfo,
- enabledProviderDataList, disabledProviderDataList, resultReceiver).getIntent();
+ enabledProviderDataList, disabledProviderDataList, resultReceiver,
+ userId).getIntent();
}
/**
@@ -142,10 +148,10 @@ public class IntentFactory {
@NonNull
public static Intent createCancelUiIntent(@NonNull Context context,
@NonNull IBinder requestToken, boolean shouldShowCancellationUi,
- @NonNull String appPackageName) {
+ @NonNull String appPackageName, @UserIdInt int userId) {
Intent intent = new Intent();
IntentCreationResult.Builder intentResultBuilder = new IntentCreationResult.Builder(intent);
- setCredentialSelectorUiComponentName(context, intent, intentResultBuilder);
+ setCredentialSelectorUiComponentName(context, intent, intentResultBuilder, userId);
intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST,
new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi,
appPackageName));
@@ -162,10 +168,10 @@ public class IntentFactory {
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver) {
+ @NonNull ResultReceiver resultReceiver, @UserIdInt int userId) {
Intent intent = new Intent();
IntentCreationResult.Builder intentResultBuilder = new IntentCreationResult.Builder(intent);
- setCredentialSelectorUiComponentName(context, intent, intentResultBuilder);
+ setCredentialSelectorUiComponentName(context, intent, intentResultBuilder, userId);
intent.putParcelableArrayListExtra(
ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -175,9 +181,11 @@ public class IntentFactory {
}
private static void setCredentialSelectorUiComponentName(@NonNull Context context,
- @NonNull Intent intent, @NonNull IntentCreationResult.Builder intentResultBuilder) {
+ @NonNull Intent intent, @NonNull IntentCreationResult.Builder intentResultBuilder,
+ @UserIdInt int userId) {
if (configurableSelectorUiEnabled()) {
- ComponentName componentName = getOemOverrideComponentName(context, intentResultBuilder);
+ ComponentName componentName = getOemOverrideComponentName(context,
+ intentResultBuilder, userId);
ComponentName fallbackUiComponentName = null;
try {
@@ -210,7 +218,7 @@ public class IntentFactory {
*/
@Nullable
private static ComponentName getOemOverrideComponentName(@NonNull Context context,
- @NonNull IntentCreationResult.Builder intentResultBuilder) {
+ @NonNull IntentCreationResult.Builder intentResultBuilder, @UserIdInt int userId) {
ComponentName result = null;
String oemComponentString =
Resources.getSystem()
@@ -228,35 +236,43 @@ public class IntentFactory {
if (oemComponentName != null) {
try {
intentResultBuilder.setOemUiPackageName(oemComponentName.getPackageName());
- ActivityInfo info = context.getPackageManager().getActivityInfo(
- oemComponentName,
- PackageManager.ComponentInfoFlags.of(
- PackageManager.MATCH_SYSTEM_ONLY));
- boolean oemComponentEnabled = info.enabled;
- int runtimeComponentEnabledState = context.getPackageManager()
+ ActivityInfo info;
+ if (android.credentials.flags.Flags.propagateUserContextForIntentCreation()) {
+ info = context.getPackageManager().getActivityInfo(oemComponentName,
+ PackageManager.ComponentInfoFlags.of(
+ PackageManager.MATCH_SYSTEM_ONLY));
+ } else {
+ info = AppGlobals.getPackageManager().getActivityInfo(
+ oemComponentName, 0, userId);
+ }
+ boolean oemComponentEnabled = false;
+ if (info != null) {
+ oemComponentEnabled = info.enabled;
+ int runtimeComponentEnabledState = context.getPackageManager()
.getComponentEnabledSetting(oemComponentName);
- if (runtimeComponentEnabledState == PackageManager
+ if (runtimeComponentEnabledState == PackageManager
.COMPONENT_ENABLED_STATE_ENABLED) {
- oemComponentEnabled = true;
- } else if (runtimeComponentEnabledState == PackageManager
+ oemComponentEnabled = true;
+ } else if (runtimeComponentEnabledState == PackageManager
.COMPONENT_ENABLED_STATE_DISABLED) {
oemComponentEnabled = false;
- }
- if (oemComponentEnabled && info.exported) {
+ }
+ if (oemComponentEnabled && info.exported) {
intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
- .OemUiUsageStatus.SUCCESS);
+ .OemUiUsageStatus.SUCCESS);
Slog.i(TAG,
- "Found enabled oem CredMan UI component."
- + oemComponentString);
+ "Found enabled oem CredMan UI component."
+ + oemComponentString);
result = oemComponentName;
- } else {
- intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
- .OemUiUsageStatus.OEM_UI_CONFIG_SPECIFIED_FOUND_BUT_NOT_ENABLED);
- Slog.i(TAG,
- "Found enabled oem CredMan UI component but it was not "
- + "enabled.");
+ } else {
+ intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
+ .OemUiUsageStatus.OEM_UI_CONFIG_SPECIFIED_FOUND_BUT_NOT_ENABLED);
+ Slog.i(TAG,
+ "Found enabled oem CredMan UI component but it was not "
+ + "enabled.");
+ }
}
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (RemoteException | PackageManager.NameNotFoundException e) {
intentResultBuilder.setOemUiUsageStatus(IntentCreationResult.OemUiUsageStatus
.OEM_UI_CONFIG_SPECIFIED_BUT_NOT_FOUND);
Slog.i(TAG, "Unable to find oem CredMan UI component: "
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index c59d3cea0414..ce2334a8247a 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -533,11 +533,11 @@ public final class SQLiteRawStatement implements Closeable {
}
/**
- * Return the number of columns in the current result row.
+ * Return the number of columns in the result set for the statement.
*
* @see <a href="http://sqlite.org/c3ref/column_count.html">sqlite3_column_count</a>
*
- * @return The number of columns in the result row.
+ * @return The number of columns in the result set.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
*/
public int getResultColumnCount() {
@@ -554,10 +554,16 @@ public final class SQLiteRawStatement implements Closeable {
*
* @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_type</a>
*
+ * If the row has no data then a {@link SQLiteMisuseException} is thrown. This condition can
+ * occur the last call to {@link #step()} returned false or if {@link #step()} was not called
+ * before the statement was created or after the last call to {@link #reset()}. Note that
+ * {@link SQLiteMisuseException} may be thrown for other reasons.
+ *
* @param columnIndex The index of a column in the result row. It is zero-based.
* @return The type of the value in the column of the result row.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data.
* @throws SQLiteException if a native error occurs.
*/
@SQLiteDataType
@@ -580,6 +586,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The name of the column in the result row.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
*/
@NonNull
@@ -606,6 +613,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The length, in bytes, of the value in the column.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
public int getColumnLength(int columnIndex) {
@@ -631,6 +639,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The value of the column as a blob, or null if the column is NULL.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
@Nullable
@@ -664,6 +673,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws IllegalArgumentException if the buffer is too small for offset+length.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
@@ -691,6 +701,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The value of a column as a double.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
public double getColumnDouble(int columnIndex) {
@@ -715,6 +726,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The value of the column as an int.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
public int getColumnInt(int columnIndex) {
@@ -739,6 +751,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The value of the column as an long.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
public long getColumnLong(int columnIndex) {
@@ -763,6 +776,7 @@ public final class SQLiteRawStatement implements Closeable {
* @return The value of the column as a string.
* @throws IllegalStateException if the statement is closed or this is a foreign thread.
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
+ * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
* @throws SQLiteException if a native error occurs.
*/
@NonNull
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 7efdd6dbdf41..99f331f43450 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -66,13 +66,14 @@ public class HubEndpoint {
REASON_CLOSE_ENDPOINT_SESSION_REQUESTED,
REASON_ENDPOINT_INVALID,
REASON_ENDPOINT_STOPPED,
+ REASON_PERMISSION_DENIED,
})
public @interface Reason {}
/** Unclassified failure */
public static final int REASON_FAILURE = 0;
- // The values 1 and 2 are reserved at the Context Hub HAL but not exposed to apps.
+ // The values 1-2 are reserved at the Context Hub HAL but not exposed to apps.
/** The peer rejected the request to open this endpoint session. */
public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3;
@@ -83,6 +84,11 @@ public class HubEndpoint {
/** The peer endpoint is invalid. */
public static final int REASON_ENDPOINT_INVALID = 5;
+ // The values 6-8 are reserved at the Context Hub HAL but not exposed to apps.
+
+ /** The endpoint did not have the required permissions. */
+ public static final int REASON_PERMISSION_DENIED = 9;
+
/**
* The endpoint is now stopped. The app should retrieve the endpoint info using {@link
* android.hardware.location.ContextHubManager#findEndpoints} or register updates through
@@ -107,7 +113,7 @@ public class HubEndpoint {
public void onSessionOpenRequest(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo)
+ @Nullable String serviceDescriptor)
throws RemoteException {
HubEndpointSession activeSession;
synchronized (mLock) {
@@ -128,16 +134,16 @@ public class HubEndpoint {
processSessionOpenRequestResult(
sessionId,
initiator,
- serviceInfo,
+ serviceDescriptor,
mLifecycleCallback.onSessionOpenRequest(
- initiator, serviceInfo)));
+ initiator, serviceDescriptor)));
}
}
private void processSessionOpenRequestResult(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo,
+ @Nullable String serviceDescriptor,
HubEndpointSessionResult result) {
if (result == null) {
throw new IllegalArgumentException(
@@ -145,7 +151,7 @@ public class HubEndpoint {
}
if (result.isAccepted()) {
- acceptSession(sessionId, initiator, serviceInfo);
+ acceptSession(sessionId, initiator, serviceDescriptor);
} else {
Log.i(
TAG,
@@ -162,7 +168,7 @@ public class HubEndpoint {
private void acceptSession(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo) {
+ @Nullable String serviceDescriptor) {
if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
// No longer registered?
return;
@@ -187,7 +193,7 @@ public class HubEndpoint {
HubEndpoint.this,
mAssignedHubEndpointInfo,
initiator,
- serviceInfo);
+ serviceDescriptor);
try {
// oneway call to notify system service that the request is completed
mServiceToken.openSessionRequestComplete(sessionId);
@@ -334,7 +340,6 @@ public class HubEndpoint {
@Nullable IHubEndpointMessageCallback endpointMessageCallback,
@NonNull Executor messageCallbackExecutor) {
mPendingHubEndpointInfo = pendingEndpointInfo;
-
mLifecycleCallback = endpointLifecycleCallback;
mLifecycleCallbackExecutor = lifecycleCallbackExecutor;
mMessageCallback = endpointMessageCallback;
@@ -350,7 +355,10 @@ public class HubEndpoint {
}
try {
IContextHubEndpoint serviceToken =
- service.registerEndpoint(mPendingHubEndpointInfo, mServiceCallback);
+ service.registerEndpoint(
+ mPendingHubEndpointInfo,
+ mServiceCallback,
+ mPendingHubEndpointInfo.getTag());
mAssignedHubEndpointInfo = serviceToken.getAssignedHubEndpointInfo();
mServiceToken = serviceToken;
} catch (RemoteException e) {
@@ -387,7 +395,7 @@ public class HubEndpoint {
}
/** @hide */
- public void openSession(HubEndpointInfo destinationInfo, @Nullable HubServiceInfo serviceInfo) {
+ public void openSession(HubEndpointInfo destinationInfo, @Nullable String serviceDescriptor) {
// TODO(b/378974199): Consider refactor these assertions
if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
// No longer registered?
@@ -396,18 +404,18 @@ public class HubEndpoint {
HubEndpointSession newSession;
try {
- // Request system service to assign session id.
- int sessionId = mServiceToken.openSession(destinationInfo, serviceInfo);
-
- // Save the newly created session
synchronized (mLock) {
+ // Request system service to assign session id.
+ int sessionId = mServiceToken.openSession(destinationInfo, serviceDescriptor);
+
+ // Save the newly created session
newSession =
new HubEndpointSession(
sessionId,
HubEndpoint.this,
destinationInfo,
mAssignedHubEndpointInfo,
- serviceInfo);
+ serviceDescriptor);
mActiveSessions.put(sessionId, newSession);
}
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index b3d65c1a4cae..77f937ebeabc 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -44,7 +44,7 @@ public class HubEndpointSession implements AutoCloseable {
@NonNull private final HubEndpoint mHubEndpoint;
@NonNull private final HubEndpointInfo mInitiator;
@NonNull private final HubEndpointInfo mDestination;
- @Nullable private final HubServiceInfo mServiceInfo;
+ @Nullable private final String mServiceDescriptor;
private final AtomicBoolean mIsClosed = new AtomicBoolean(true);
@@ -54,12 +54,12 @@ public class HubEndpointSession implements AutoCloseable {
@NonNull HubEndpoint hubEndpoint,
@NonNull HubEndpointInfo destination,
@NonNull HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo) {
+ @Nullable String serviceDescriptor) {
mId = id;
mHubEndpoint = hubEndpoint;
mDestination = destination;
mInitiator = initiator;
- mServiceInfo = serviceInfo;
+ mServiceDescriptor = serviceDescriptor;
}
/**
@@ -69,6 +69,8 @@ public class HubEndpointSession implements AutoCloseable {
* @return For messages that does not require a response, the transaction will immediately
* complete. For messages that requires a response, the transaction will complete after
* receiving the response for the message.
+ * @throws SecurityException if the application doesn't have the right permissions to send this
+ * message.
*/
@NonNull
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -131,8 +133,8 @@ public class HubEndpointSession implements AutoCloseable {
}
/**
- * Get the {@link HubServiceInfo} associated with this session. Null value indicates that there
- * is no service associated to this session.
+ * Get the service descriptor associated with this session. Null value indicates that there is
+ * no service associated to this session.
*
* <p>For hub initiated sessions, the object was previously used in as an argument for open
* request in {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}.
@@ -141,8 +143,8 @@ public class HubEndpointSession implements AutoCloseable {
* android.hardware.location.ContextHubManager#openSession}
*/
@Nullable
- public HubServiceInfo getServiceInfo() {
- return mServiceInfo;
+ public String getServiceDescriptor() {
+ return mServiceDescriptor;
}
@Override
diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java
index a1c52fb5864f..2f33e8f8872b 100644
--- a/core/java/android/hardware/contexthub/HubServiceInfo.java
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.java
@@ -132,6 +132,21 @@ public final class HubServiceInfo implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("Service: ");
+ out.append("descriptor=");
+ out.append(mServiceDescriptor);
+ out.append(", format=");
+ out.append(mFormat);
+ out.append(", version=");
+ out.append(Integer.toHexString(mMajorVersion));
+ out.append(".");
+ out.append(Integer.toHexString(mMinorVersion));
+ return out.toString();
+ }
+
/** Parcel implementation details */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
index 1c98b4b3f4f5..44f80c819e83 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -34,11 +34,13 @@ interface IContextHubEndpoint {
* Request system service to open a session with a specific destination.
*
* @param destination A valid HubEndpointInfo representing the destination.
+ * @param serviceDescriptor An optional descriptor of the service to scope this session to.
*
* @throws IllegalArgumentException If the HubEndpointInfo is not valid.
* @throws IllegalStateException If there are too many opened sessions.
*/
- int openSession(in HubEndpointInfo destination, in @nullable HubServiceInfo serviceInfo);
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ int openSession(in HubEndpointInfo destination, in @nullable String serviceDescriptor);
/**
* Request system service to close a specific session
@@ -48,6 +50,7 @@ interface IContextHubEndpoint {
*
* @throws IllegalStateException If the session wasn't opened.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void closeSession(int sessionId, int reason);
/**
@@ -59,11 +62,13 @@ interface IContextHubEndpoint {
*
* @throws IllegalStateException If the session wasn't opened.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void openSessionRequestComplete(int sessionId);
/**
* Unregister this endpoint from the HAL, invalidate the EndpointInfo previously assigned.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void unregister();
/**
@@ -75,6 +80,7 @@ interface IContextHubEndpoint {
* @param transactionCallback Nullable. If the hub message requires a reply, the transactionCallback
* will be set to non-null.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void sendMessage(int sessionId, in HubMessage message,
in @nullable IContextHubTransactionCallback transactionCallback);
@@ -86,5 +92,6 @@ interface IContextHubEndpoint {
* @param messageSeqNumber The message sequence number, this should match a previously received HubMessage.
* @param errorCode The message delivery status detail.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode);
}
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
index 1ae5fb9d28c1..63edda84bde5 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
@@ -29,9 +29,9 @@ oneway interface IContextHubEndpointCallback {
*
* @param sessionId An integer identifying the session, assigned by the initiator
* @param initiator HubEndpointInfo representing the requester
- * @param serviceInfo Nullable HubServiceInfo representing the service associated with this session
+ * @param serviceDescriptor Nullable string representing the service associated with this session
*/
- void onSessionOpenRequest(int sessionId, in HubEndpointInfo initiator, in @nullable HubServiceInfo serviceInfo);
+ void onSessionOpenRequest(int sessionId, in HubEndpointInfo initiator, in @nullable String serviceDescriptor);
/**
* Request from system service to close a specific session
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
index fe449bb5ce0e..698ed0adfd80 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
+++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
@@ -34,12 +34,12 @@ public interface IHubEndpointLifecycleCallback {
* Called when an endpoint is requesting a session be opened with another endpoint.
*
* @param requester The {@link HubEndpointInfo} object representing the requester
- * @param serviceInfo The {@link HubServiceInfo} object representing the service associated with
- * this session. Null indicates that there is no service associated with this session.
+ * @param serviceDescriptor A string describing the service associated with this session. Null
+ * indicates that there is no service associated with this session.
*/
@NonNull
HubEndpointSessionResult onSessionOpenRequest(
- @NonNull HubEndpointInfo requester, @Nullable HubServiceInfo serviceInfo);
+ @NonNull HubEndpointInfo requester, @Nullable String serviceDescriptor);
/**
* Called when a communication session is opened and ready to be used.
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 54d0dd0eb8f8..211aefffa34c 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -91,6 +91,15 @@ public final class DisplayTopology implements Parcelable {
@VisibleForTesting
public DisplayTopology(TreeNode root, int primaryDisplayId) {
mRoot = root;
+ if (mRoot != null) {
+ // Set mRoot's position and offset to predictable values, just so we don't leak state
+ // from some previous arrangement the node was used in, or leak arbitrary values passed
+ // to the TreeNode constructor. The position and offset don't mean anything because
+ // mRoot doesn't have a parent.
+ mRoot.mPosition = POSITION_LEFT;
+ mRoot.mOffset = 0f;
+ }
+
mPrimaryDisplayId = primaryDisplayId;
}
@@ -422,6 +431,14 @@ public final class DisplayTopology implements Parcelable {
}
}
}
+
+ // Sort children lists by display ID.
+ final Comparator<TreeNode> idComparator = (d1, d2) -> {
+ return Integer.compare(d1.mDisplayId, d2.mDisplayId);
+ };
+ for (TreeNode display : displays) {
+ display.mChildren.sort(idComparator);
+ }
}
/**
@@ -582,6 +599,15 @@ public final class DisplayTopology implements Parcelable {
}
}
+ /** Returns the graph representation of the topology */
+ public DisplayTopologyGraph getGraph() {
+ // TODO(b/364907904): implement
+ return new DisplayTopologyGraph(mPrimaryDisplayId,
+ new DisplayTopologyGraph.DisplayNode[] { new DisplayTopologyGraph.DisplayNode(
+ mRoot == null ? Display.DEFAULT_DISPLAY : mRoot.mDisplayId,
+ new DisplayTopologyGraph.AdjacentDisplay[0])});
+ }
+
/**
* Tests whether two brightness float values are within a small enough tolerance
* of each other.
diff --git a/core/java/android/hardware/display/DisplayTopologyGraph.java b/core/java/android/hardware/display/DisplayTopologyGraph.java
new file mode 100644
index 000000000000..938e6d108f5d
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayTopologyGraph.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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 android.hardware.display;
+
+/**
+ * Graph of the displays in {@link android.hardware.display.DisplayTopology} tree.
+ *
+ * @hide
+ */
+public record DisplayTopologyGraph(int primaryDisplayId, DisplayNode[] displayNodes) {
+ /**
+ * Display in the topology
+ */
+ public record DisplayNode(
+ int displayId,
+ AdjacentDisplay[] adjacentDisplays) {}
+
+ /**
+ * Edge to adjacent display
+ */
+ public record AdjacentDisplay(
+ // The logical Id of this adjacent display
+ int displayId,
+ // Side of the other display which touches this adjacent display.
+ @DisplayTopology.TreeNode.Position
+ int position,
+ // How many px this display is shifted along the touchingSide, can be negative.
+ float offsetPx) {}
+}
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 3f9317aa24f1..f8f7f5e0586e 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -28,7 +28,6 @@ import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
import static com.android.hardware.input.Flags.touchpadSystemGestureDisable;
-import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -366,15 +365,6 @@ public class InputSettings {
}
/**
- * Returns true if the feature flag for touchpad tap dragging is enabled.
- *
- * @hide
- */
- public static boolean isTouchpadTapDraggingFeatureFlagEnabled() {
- return touchpadTapDragging();
- }
-
- /**
* Returns true if the feature flag for disabling system gestures on touchpads is enabled.
*
* @hide
@@ -461,9 +451,6 @@ public class InputSettings {
* @hide
*/
public static boolean useTouchpadTapDragging(@NonNull Context context) {
- if (!isTouchpadTapDraggingFeatureFlagEnabled()) {
- return false;
- }
return Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.TOUCHPAD_TAP_DRAGGING, 0, UserHandle.USER_CURRENT) == 1;
}
@@ -480,9 +467,6 @@ public class InputSettings {
*/
@RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public static void setTouchpadTapDragging(@NonNull Context context, boolean enabled) {
- if (!isTouchpadTapDraggingFeatureFlagEnabled()) {
- return;
- }
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.TOUCHPAD_TAP_DRAGGING, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 711dc3a2cf7c..47ef461dd53d 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -43,6 +43,8 @@ public final class KeyGestureEvent {
private static final int LOG_EVENT_UNSPECIFIED =
FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+ // These values should not change and values should not be re-used as this data is persisted to
+ // long term storage and must be kept backwards compatible.
public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
public static final int KEY_GESTURE_TYPE_HOME = 1;
public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
@@ -122,6 +124,7 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74;
public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77;
public static final int FLAG_CANCELLED = 1;
@@ -213,7 +216,8 @@ public final class KeyGestureEvent {
KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
- KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW
+ KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -786,6 +790,8 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK";
case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
+ case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
+ return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index ebb617249993..313bad50e88e 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -37,13 +37,6 @@ flag {
flag {
namespace: "input_native"
- name: "touchpad_tap_dragging"
- description: "Offers a setting to enable touchpad tap dragging"
- bug: "321978150"
-}
-
-flag {
- namespace: "input_native"
name: "keyboard_glyph_map"
description: "Allows system to provide keyboard specific key drawables and shortcuts via config files"
bug: "345440920"
@@ -196,4 +189,18 @@ flag {
namespace: "wallet_integration"
description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
bug: "378736024"
- } \ No newline at end of file
+ }
+
+flag {
+ name: "pointer_acceleration"
+ namespace: "input"
+ description: "Allows the user to disable pointer acceleration for mouse and touchpads."
+ bug: "349006858"
+}
+
+flag {
+ name: "mouse_scrolling_acceleration"
+ namespace: "input"
+ description: "Allows the user to disable input scrolling acceleration for mouse."
+ bug: "383555305"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 3a74130d5e83..1e0cc94612dd 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -754,6 +754,7 @@ public final class ContextHubManager {
* @param executor the executor to invoke callbacks for this client
* @return the callback interface
*/
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
private IContextHubEndpointDiscoveryCallback createDiscoveryCallback(
IHubEndpointDiscoveryCallback callback,
Executor executor,
@@ -767,21 +768,9 @@ public final class ContextHubManager {
}
executor.execute(
() -> {
- // TODO(b/380293951): Refactor
List<HubDiscoveryInfo> discoveryList =
- new ArrayList<>(hubEndpointInfoList.length);
- for (HubEndpointInfo info : hubEndpointInfoList) {
- if (serviceDescriptor != null) {
- for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
- if (sInfo.getServiceDescriptor()
- .equals(serviceDescriptor)) {
- discoveryList.add(new HubDiscoveryInfo(info, sInfo));
- }
- }
- } else {
- discoveryList.add(new HubDiscoveryInfo(info));
- }
- }
+ getMatchingEndpointDiscoveryList(
+ hubEndpointInfoList, serviceDescriptor);
if (discoveryList.isEmpty()) {
Log.w(TAG, "onEndpointsStarted: no matching service descriptor");
} else {
@@ -799,19 +788,8 @@ public final class ContextHubManager {
executor.execute(
() -> {
List<HubDiscoveryInfo> discoveryList =
- new ArrayList<>(hubEndpointInfoList.length);
- for (HubEndpointInfo info : hubEndpointInfoList) {
- if (serviceDescriptor != null) {
- for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
- if (sInfo.getServiceDescriptor()
- .equals(serviceDescriptor)) {
- discoveryList.add(new HubDiscoveryInfo(info, sInfo));
- }
- }
- } else {
- discoveryList.add(new HubDiscoveryInfo(info));
- }
- }
+ getMatchingEndpointDiscoveryList(
+ hubEndpointInfoList, serviceDescriptor);
if (discoveryList.isEmpty()) {
Log.w(TAG, "onEndpointsStopped: no matching service descriptor");
} else {
@@ -823,6 +801,34 @@ public final class ContextHubManager {
}
/**
+ * Generates a list of matching endpoint discovery info, given the list and an (optional)
+ * service descriptor. If service descriptor is null, all endpoints are added to the filtered
+ * output list.
+ *
+ * @param hubEndpointInfoList The hub endpoints to filter.
+ * @param serviceDescriptor The optional service descriptor to match, null if adding all
+ * endpoints.
+ * @return The list of filtered HubDiscoveryInfo which matches the serviceDescriptor.
+ */
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ private List<HubDiscoveryInfo> getMatchingEndpointDiscoveryList(
+ HubEndpointInfo[] hubEndpointInfoList, @Nullable String serviceDescriptor) {
+ List<HubDiscoveryInfo> discoveryList = new ArrayList<>(hubEndpointInfoList.length);
+ for (HubEndpointInfo info : hubEndpointInfoList) {
+ if (serviceDescriptor != null) {
+ for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
+ if (sInfo.getServiceDescriptor().equals(serviceDescriptor)) {
+ discoveryList.add(new HubDiscoveryInfo(info, sInfo));
+ }
+ }
+ } else {
+ discoveryList.add(new HubDiscoveryInfo(info));
+ }
+ }
+ return discoveryList;
+ }
+
+ /**
* Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback,
* Executor)} with the default executor in the main thread.
*/
@@ -841,6 +847,7 @@ public final class ContextHubManager {
* @param endpointId The identifier of the hub endpoint.
* @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -881,6 +888,7 @@ public final class ContextHubManager {
* @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
* @throws IllegalArgumentException if the serviceDescriptor is empty.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -911,6 +919,7 @@ public final class ContextHubManager {
*
* @param callback The callback previously registered.
* @throws IllegalArgumentException If the callback was not previously registered.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -1309,16 +1318,18 @@ public final class ContextHubManager {
* ContextHubManager#registerEndpoint(HubEndpoint)}.
* @param destination {@link HubEndpointInfo} object that represents an endpoint from previous
* endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}).
- * @param serviceInfo {@link HubServiceInfo} object that describes the service associated with
- * this session. The information will be sent to the destination as part of open request.
+ * @param serviceDescriptor A string that describes the service associated with this session.
+ * The information will be sent to the destination as part of open request.
+ * @throws IllegalStateException if hubEndpoint was not successfully registered, or if there is
+ * insufficient capacity for creating a session.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void openSession(
@NonNull HubEndpoint hubEndpoint,
@NonNull HubEndpointInfo destination,
- @NonNull HubServiceInfo serviceInfo) {
- hubEndpoint.openSession(destination, serviceInfo);
+ @NonNull String serviceDescriptor) {
+ hubEndpoint.openSession(destination, serviceDescriptor);
}
/**
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index f14aadcab474..2a472375a00f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -137,7 +137,7 @@ interface IContextHubService {
// Register an endpoint with the context hub
@EnforcePermission("ACCESS_CONTEXT_HUB")
- IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback);
+ IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback, String packageName);
// Register an endpoint discovery callback (id)
@EnforcePermission("ACCESS_CONTEXT_HUB")
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 42028f67f400..e5717ac87d88 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -43,22 +43,20 @@ import java.util.stream.Stream;
* <li>DAB channel info</li>
* </ui>
*
- * <p>The primary ID uniquely identifies a station and can be used for equality
- * check. The secondary IDs are supplementary and can speed up tuning process,
- * but the primary ID is sufficient (ie. after a full band scan).
- *
- * <p>Two selectors with different secondary IDs, but the same primary ID are
- * considered equal. In particular, secondary IDs vector may get updated for
+ * <p>Except for DAB radio, two selectors with different secondary IDs, but the same primary
+ * ID are considered equal. In particular, secondary IDs vector may get updated for
* an entry on the program list (ie. when a better frequency for a given
- * station is found).
+ * station is found). For DAB radio, two selectors with the same primary ID and the same
+ * DAB frequency and DAB ensemble secondary IDs (if exist) are considered equal.
*
* <p>The primaryId of a given programType MUST be of a specific type:
* <ui>
- * <li>AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;</li>
- * <li>AM_HD, FM_HD: HD_STATION_ID_EXT;</li>
- * <li>DAB: DAB_SIDECC;</li>
- * <li>DRMO: DRMO_SERVICE_ID;</li>
- * <li>SXM: SXM_SERVICE_ID;</li>
+ * <li>AM, FM: {@link #IDENTIFIER_TYPE_RDS_PI} if the station broadcasts RDS,
+ * {@link #IDENTIFIER_TYPE_AMFM_FREQUENCY} otherwise;</li>
+ * <li>AM_HD, FM_HD: {@link #IDENTIFIER_TYPE_HD_STATION_ID_EXT};</li>
+ * <li>DAB: {@link #IDENTIFIER_TYPE_DAB_SID_EXT} or
+ * {@link #IDENTIFIER_TYPE_DAB_DMB_SID_EXT};</li>
+ * <li>DRMO: {@link #IDENTIFIER_TYPE_DRMO_SERVICE_ID};</li>
* <li>VENDOR: VENDOR_PRIMARY.</li>
* </ui>
* @hide
@@ -597,9 +595,9 @@ public final class ProgramSelector implements Parcelable {
* negatives. In particular, it may be way off for certain regions.
* The main purpose is to avoid passing improper units, ie. MHz instead of kHz.
*
- * @param isAm true, if AM, false if FM.
+ * @param isAm {@code true}, if AM, {@code false} if FM.
* @param frequencyKhz the frequency in kHz.
- * @return true, if the frequency is roughly valid.
+ * @return {@code true}, if the frequency is roughly valid.
*/
private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
if (isAm) {
@@ -785,8 +783,8 @@ public final class ProgramSelector implements Parcelable {
* ProgramLists for category entries.
*
* @see ProgramList.Filter#areCategoriesIncluded
- * @return False if this identifier's type is not tunable (e.g. DAB ensemble or
- * vendor-specified type). True otherwise.
+ * @return {@link false} if this identifier's type is not tunable (e.g. DAB ensemble or
+ * vendor-specified type). {@link true} otherwise.
*/
public boolean isCategoryType() {
return (mType >= IDENTIFIER_TYPE_VENDOR_START && mType <= IDENTIFIER_TYPE_VENDOR_END)
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 9b37533f5b02..9badbf8e2a1b 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -299,9 +299,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
if (event.hasNoModifiers()) {
return false;
}
- return event.hasModifiers(KeyEvent.META_CTRL_ON)
- || event.hasModifiers(KeyEvent.META_ALT_ON)
- || event.hasModifiers(KeyEvent.KEYCODE_FUNCTION);
+ return event.isCtrlPressed()
+ || event.isAltPressed()
+ || event.isFunctionPressed()
+ || event.isMetaPressed();
}
private boolean needsVerification(KeyEvent event) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5f3c15d1842e..4c9e73c8b21f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -3202,7 +3202,8 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@FlaggedApi(Flags.FLAG_ADAPTIVE_HANDWRITING_BOUNDS)
public final void setStylusHandwritingRegion(@NonNull Region handwritingRegion) {
- if (handwritingRegion.equals(mLastHandwritingRegion)) {
+ final Region immutableHandwritingRegion = new Region(handwritingRegion);
+ if (immutableHandwritingRegion.equals(mLastHandwritingRegion)) {
Log.v(TAG, "Failed to set setStylusHandwritingRegion():"
+ " same region set twice.");
return;
@@ -3210,10 +3211,10 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) {
Log.d(TAG, "Setting new handwriting region for stylus handwriting "
- + handwritingRegion + " from last " + mLastHandwritingRegion);
+ + immutableHandwritingRegion + " from last " + mLastHandwritingRegion);
}
- mPrivOps.setHandwritingTouchableRegion(handwritingRegion);
- mLastHandwritingRegion = handwritingRegion;
+ mPrivOps.setHandwritingTouchableRegion(immutableHandwritingRegion);
+ mLastHandwritingRegion = immutableHandwritingRegion;
}
/**
diff --git a/core/java/android/net/EventLogTags.logtags b/core/java/android/net/EventLogTags.logtags
index d5ed01496eba..32953c92d120 100644
--- a/core/java/android/net/EventLogTags.logtags
+++ b/core/java/android/net/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.net
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 95b5f697969e..8d12b76e23ff 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -31,9 +31,9 @@ flag {
}
flag {
- name: "x509_extensions_certificate_transparency"
+ name: "mdns_improvement_for_25q2"
is_exported: true
- namespace: "network_security"
- description: "Flag to use checkServerTrusted to verify SCTs in OCSP and TLS Data"
- bug: "319829948"
+ namespace: "android_core_networking"
+ description: "Flag for MDNS quality, reliability and performance improvement in 25Q2"
+ bug: "373270045"
}
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index b44f75a585d5..3425b77be954 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -22,7 +22,7 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.net.platform.flags.Flags;
+import android.security.Flags;
import android.security.net.config.UserCertificateSource;
import com.android.org.conscrypt.TrustManagerImpl;
@@ -152,7 +152,7 @@ public class X509TrustManagerExtensions {
* @throws IllegalArgumentException if the TrustManager is not compatible.
* @return the properly ordered chain used for verification as a list of X509Certificates.
*/
- @FlaggedApi(Flags.FLAG_X509_EXTENSIONS_CERTIFICATE_TRANSPARENCY)
+ @FlaggedApi(Flags.FLAG_CERTIFICATE_TRANSPARENCY_CONFIGURATION)
@NonNull
public List<X509Certificate> checkServerTrusted(
@SuppressLint("ArrayReturn") @NonNull X509Certificate[] chain,
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index bf351ce07fe8..f53d1c4d191d 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -62,7 +62,11 @@ final public class DnsEvent {
return isSuccess;
}
if (eventCount == eventTypes.length) {
- resize((int) (1.4 * eventCount));
+ int resizeLength = (int) (1.4 * eventCount);
+ if (eventCount == resizeLength) {
+ resizeLength++;
+ }
+ resize(resizeLength);
}
eventTypes[eventCount] = eventType;
returnCodes[eventCount] = returnCode;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8b6da7e0ae58..d54dbad9286c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -403,7 +403,7 @@ public class Build {
* increase when the hardware manufacturer provides an OTA update.
* <p>
* This constant records the major version of Android. Use {@link
- * SDK_INT_FULL} if you need to consider the minor version of Android
+ * #SDK_INT_FULL} if you need to consider the minor version of Android
* as well.
* <p>
* Possible values are defined in {@link Build.VERSION_CODES}.
@@ -1558,6 +1558,7 @@ public class Build {
* @hide
*/
@SuppressWarnings("FlaggedApi") // SDK_INT_MULTIPLIER is defined in this file
+ @SuppressLint("InlinedApi")
public static @SdkIntFull int parseFullVersion(@NonNull String version) {
int index = version.indexOf('.');
int major;
@@ -1569,12 +1570,22 @@ public class Build {
major = Integer.parseInt(version.substring(0, index));
minor = Integer.parseInt(version.substring(index + 1));
}
- if (major < 0 || minor < 0) {
- throw new NumberFormatException();
+ if (major < 0) {
+ throw new NumberFormatException("negative major version");
+ }
+ if (major >= 21474) {
+ throw new NumberFormatException("major version too large, must be less than 21474");
+ }
+ if (minor < 0) {
+ throw new NumberFormatException("negative minor version");
+ }
+ if (minor >= VERSION_CODES_FULL.SDK_INT_MULTIPLIER) {
+ throw new NumberFormatException("minor version too large, must be less than "
+ + VERSION_CODES_FULL.SDK_INT_MULTIPLIER);
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("failed to parse '" + version
- + "' as a major.minor version code");
+ + "' as a major.minor version code", e);
}
return major * VERSION_CODES_FULL.SDK_INT_MULTIPLIER + minor;
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 05bd10b053fe..819d58d9f059 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -70,6 +70,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*/
static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13;
+ /**
+ * Indicates the bundle definitely contains an Intent.
+ */
+ static final int FLAG_HAS_INTENT = 1 << 14;
+
/**
* Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
@@ -118,6 +123,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
public static final Bundle EMPTY;
/**
+ * @hide
+ */
+ public static Class<?> intentClass;
+
+ /**
* Special extras used to denote extras have been stripped off.
* @hide
*/
@@ -388,6 +398,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
if ((bundle.mFlags & FLAG_HAS_BINDERS_KNOWN) == 0) {
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
+ mFlags |= bundle.mFlags & FLAG_HAS_INTENT;
}
/**
@@ -447,6 +458,16 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
}
+ /**
+ * Returns if the bundle definitely contains at least an intent. This method returns false does
+ * not guarantee the bundle does not contain a nested intent. An intent could still exist in a
+ * ParcelableArrayList, ParcelableArray, ParcelableList, a bundle in this bundle, etc.
+ * @hide
+ */
+ public boolean hasIntent() {
+ return (mFlags & FLAG_HAS_INTENT) != 0;
+ }
+
/** {@hide} */
@Override
public void putObject(@Nullable String key, @Nullable Object value) {
@@ -569,6 +590,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
+ if (intentClass != null && intentClass.isInstance(value)) {
+ mFlags |= FLAG_HAS_INTENT;
+ }
}
/**
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 804e8faeef16..230fa3fec930 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -18,6 +18,8 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.Instrumentation;
@@ -27,11 +29,14 @@ import android.os.UserHandle;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
+import android.ravenwood.annotation.RavenwoodThrow;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.NeverCompile;
@@ -174,6 +179,28 @@ public final class MessageQueue {
}
}
+ @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;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
private static boolean isInstrumenting() {
final ActivityThread activityThread = ActivityThread.currentActivityThread();
if (activityThread == null) {
@@ -203,12 +230,9 @@ public final class MessageQueue {
private static final class MatchDeliverableMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.when <= when) {
- return true;
- }
- return false;
+ return n.mMessage.when <= when;
}
}
private final MatchDeliverableMessages mMatchDeliverableMessages =
@@ -355,7 +379,7 @@ public final class MessageQueue {
* @see OnFileDescriptorEventListener
* @see #removeOnFileDescriptorEventListener
*/
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
@@ -389,7 +413,7 @@ public final class MessageQueue {
* @see OnFileDescriptorEventListener
* @see #addOnFileDescriptorEventListener
*/
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
@@ -405,7 +429,7 @@ public final class MessageQueue {
}
}
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
@@ -528,7 +552,7 @@ public final class MessageQueue {
/* This is only read/written from the Looper thread. For use with Concurrent MQ */
private int mNextPollTimeoutMillis;
private boolean mMessageDirectlyQueued;
- private Message nextMessage() {
+ private Message nextMessage(boolean peek) {
int i = 0;
while (true) {
@@ -690,7 +714,7 @@ public final class MessageQueue {
if (sState.compareAndSet(this, sStackStateActive, nextOp)) {
mMessageCounts.clearCounts();
if (found != null) {
- if (!removeFromPriorityQueue(found)) {
+ if (!peek && !removeFromPriorityQueue(found)) {
/*
* RemoveMessages() might be able to pull messages out from under us
* However we can detect that here and just loop around if it happens.
@@ -724,7 +748,7 @@ public final class MessageQueue {
mMessageDirectlyQueued = false;
nativePollOnce(ptr, mNextPollTimeoutMillis);
- Message msg = nextMessage();
+ Message msg = nextMessage(false);
if (msg != null) {
msg.markInUse();
return msg;
@@ -1043,8 +1067,9 @@ public final class MessageQueue {
}
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == null && m.arg1 == mBarrierToken) {
return true;
}
@@ -1247,10 +1272,158 @@ public final class MessageQueue {
return true;
}
+ private Message legacyPeekOrPoll(boolean peek) {
+ synchronized (this) {
+ // Try to retrieve the next message. Return if found.
+ final long now = SystemClock.uptimeMillis();
+ Message prevMsg = null;
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ // Stalled by a barrier. Find the next asynchronous message in the queue.
+ do {
+ prevMsg = msg;
+ msg = msg.next;
+ } while (msg != null && !msg.isAsynchronous());
+ }
+ if (msg != null) {
+ if (now >= msg.when) {
+ // Got a message.
+ mBlocked = false;
+ if (peek) {
+ return msg;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
+ }
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
+ }
+ }
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret;
+ if (mUseConcurrent) {
+ ret = nextMessage(true);
+ } else {
+ ret = legacyPeekOrPoll(true);
+ }
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ @Nullable
+ Message pollForTest() {
+ throwIfNotTest();
+ if (mUseConcurrent) {
+ return nextMessage(false);
+ } else {
+ return legacyPeekOrPoll(false);
+ }
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ *
+ * Calls to this method must not be allowed to race with `next`.
+ * Specifically, the Looper thread must be paused before calling this method,
+ * and may not be resumed until after returning from this method.
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+ if (mUseConcurrent) {
+ // Call nextMessage to get the stack drained into our priority queues
+ nextMessage(true);
+
+ Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
+ MessageNode queueNode = iterateNext(queueIter);
+
+ if (queueNode != null && queueNode.isBarrier()) {
+ long now = SystemClock.uptimeMillis();
+
+ /* Look for a deliverable async node. If one exists we are not blocked. */
+ Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator();
+ MessageNode asyncNode = iterateNext(asyncQueueIter);
+ if (asyncNode != null && now >= asyncNode.getWhen()) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ while (queueNode != null && queueNode.isBarrier()) {
+ queueNode = iterateNext(queueIter);
+ }
+ if (queueNode != null && now >= queueNode.getWhen()) {
+ return true;
+ }
+ }
+ } else {
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ Message iter = msg;
+ /* Look for a deliverable async node */
+ do {
+ iter = iter.next;
+ } while (iter != null && !iter.isAsynchronous());
+
+ long now = SystemClock.uptimeMillis();
+ if (iter != null && now >= iter.when) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ iter = msg;
+ do {
+ iter = iter.next;
+ } while (iter != null && (iter.target == null || iter.isAsynchronous()));
+
+ if (iter != null && now >= iter.when) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || m.obj == object)) {
return true;
}
@@ -1281,8 +1454,9 @@ public final class MessageQueue {
private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1314,8 +1488,9 @@ public final class MessageQueue {
private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
return true;
}
@@ -1348,12 +1523,9 @@ public final class MessageQueue {
private static final class MatchHandler extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.target == h) {
- return true;
- }
- return false;
+ return n.mMessage.target == h;
}
}
private final MatchHandler mMatchHandler = new MatchHandler();
@@ -1531,8 +1703,9 @@ public final class MessageQueue {
private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1594,8 +1767,9 @@ public final class MessageQueue {
private static final class MatchHandlerAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || m.obj == object)) {
return true;
}
@@ -1655,8 +1829,9 @@ public final class MessageQueue {
private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1762,7 +1937,7 @@ public final class MessageQueue {
private static final class MatchAllMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
return true;
}
@@ -1774,9 +1949,10 @@ public final class MessageQueue {
private static final class MatchAllFutureMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.when > when) {
+ final Message m = n.mMessage;
+ if (m.when > when) {
return true;
}
return false;
@@ -2482,7 +2658,7 @@ public final class MessageQueue {
* This class is used to find matches for hasMessages() and removeMessages()
*/
private abstract static class MessageCompare {
- public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+ public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object,
Runnable r, long when);
}
@@ -2517,7 +2693,7 @@ public final class MessageQueue {
MessageNode p = (MessageNode) top;
while (true) {
- if (compare.compareMessage(p.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(p, h, what, object, r, when)) {
found = true;
if (DEBUG) {
Log.d(TAG_C, "stackHasMessages node matches");
@@ -2562,7 +2738,7 @@ public final class MessageQueue {
while (iterator.hasNext()) {
MessageNode msg = iterator.next();
- if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(msg, h, what, object, r, when)) {
if (removeMatches) {
found = true;
if (queue.remove(msg)) {
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index c2a47d767801..d7d8e4199b33 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -16,9 +16,13 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -364,6 +368,28 @@ public final class MessageQueue {
mPtr = nativeInit();
}
+ @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;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -384,8 +410,9 @@ public final class MessageQueue {
private static final class MatchDeliverableMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.when <= when) {
return true;
}
@@ -562,7 +589,7 @@ public final class MessageQueue {
private static final AtomicLong mMessagesDelivered = new AtomicLong();
private boolean mMessageDirectlyQueued;
- private Message nextMessage() {
+ private Message nextMessage(boolean peek) {
int i = 0;
while (true) {
@@ -724,7 +751,7 @@ public final class MessageQueue {
if (sState.compareAndSet(this, sStackStateActive, nextOp)) {
mMessageCounts.clearCounts();
if (found != null) {
- if (!removeFromPriorityQueue(found)) {
+ if (!peek && !removeFromPriorityQueue(found)) {
/*
* RemoveMessages() might be able to pull messages out from under us
* However we can detect that here and just loop around if it happens.
@@ -758,7 +785,7 @@ public final class MessageQueue {
mMessageDirectlyQueued = false;
nativePollOnce(ptr, mNextPollTimeoutMillis);
- Message msg = nextMessage();
+ Message msg = nextMessage(false);
if (msg != null) {
msg.markInUse();
return msg;
@@ -993,8 +1020,9 @@ public final class MessageQueue {
}
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == null && m.arg1 == mBarrierToken) {
return true;
}
@@ -1039,6 +1067,84 @@ public final class MessageQueue {
}
}
+ private static final class MatchEarliestMessage extends MessageCompare {
+ MessageNode mEarliest = null;
+
+ @Override
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
+ if (mEarliest == null || mEarliest.mMessage.when > m.when) {
+ mEarliest = n;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret = nextMessage(true);
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @Nullable
+ Message pollForTest() {
+ throwIfNotTest();
+ return nextMessage(false);
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ *
+ * Calls to this method must not be allowed to race with `next`.
+ * Specifically, the Looper thread must be paused before calling this method,
+ * and may not be resumed until after returning from this method.
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+
+ // Call nextMessage to get the stack drained into our priority queues
+ nextMessage(true);
+
+ Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
+ MessageNode queueNode = iterateNext(queueIter);
+
+ if (queueNode != null && queueNode.isBarrier()) {
+ long now = SystemClock.uptimeMillis();
+
+ /* Look for a deliverable async node. If one exists we are not blocked. */
+ Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator();
+ MessageNode asyncNode = iterateNext(asyncQueueIter);
+ if (asyncNode != null && now >= asyncNode.getWhen()) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ while (queueNode != null && queueNode.isBarrier()) {
+ queueNode = iterateNext(queueIter);
+ }
+ if (queueNode != null && now >= queueNode.getWhen()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private StateNode getStateNode(StackNode node) {
if (node.isMessageNode()) {
return ((MessageNode) node).mBottomOfStack;
@@ -1058,7 +1164,7 @@ public final class MessageQueue {
* This class is used to find matches for hasMessages() and removeMessages()
*/
private abstract static class MessageCompare {
- public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+ public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object,
Runnable r, long when);
}
@@ -1093,7 +1199,7 @@ public final class MessageQueue {
MessageNode p = (MessageNode) top;
while (true) {
- if (compare.compareMessage(p.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(p, h, what, object, r, when)) {
found = true;
if (DEBUG) {
Log.w(TAG, "stackHasMessages node matches");
@@ -1138,7 +1244,7 @@ public final class MessageQueue {
while (iterator.hasNext()) {
MessageNode msg = iterator.next();
- if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(msg, h, what, object, r, when)) {
if (removeMatches) {
found = true;
if (queue.remove(msg)) {
@@ -1167,8 +1273,9 @@ public final class MessageQueue {
private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || m.obj == object)) {
return true;
}
@@ -1187,8 +1294,9 @@ public final class MessageQueue {
private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1208,8 +1316,9 @@ public final class MessageQueue {
private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
return true;
}
@@ -1229,8 +1338,9 @@ public final class MessageQueue {
private static final class MatchHandler extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h) {
return true;
}
@@ -1268,8 +1378,9 @@ public final class MessageQueue {
private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1287,8 +1398,9 @@ public final class MessageQueue {
private static final class MatchHandlerAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || m.obj == object)) {
return true;
}
@@ -1305,8 +1417,9 @@ public final class MessageQueue {
private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1324,8 +1437,8 @@ public final class MessageQueue {
private static final class MatchAllMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
return true;
}
}
@@ -1336,8 +1449,9 @@ public final class MessageQueue {
private static final class MatchAllFutureMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.when > when) {
return true;
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ef1e6c9405f3..2bc6ab5a18e9 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.AppGlobals;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.graphics.Bitmap;
import android.util.Log;
import com.android.internal.util.FastPrintWriter;
@@ -2139,6 +2140,47 @@ public final class Debug
}
/**
+ * Like dumpHprofData(String), but takes an argument of bitmapFormat,
+ * which can be png, jpg, webp, or null (no bitmaps in heapdump).
+ *
+ * @hide
+ */
+ public static void dumpHprofData(String fileName, String bitmapFormat)
+ throws IOException {
+ try {
+ if (bitmapFormat != null) {
+ Bitmap.dumpAll(bitmapFormat);
+ }
+ VMDebug.dumpHprofData(fileName);
+ } finally {
+ if (bitmapFormat != null) {
+ Bitmap.dumpAll(null); // clear dump data
+ }
+ }
+ }
+
+ /**
+ * Like dumpHprofData(String, FileDescriptor), but takes an argument
+ * of bitmapFormat, which can be png, jpg, webp, or null (no bitmaps
+ * in heapdump).
+ *
+ * @hide
+ */
+ public static void dumpHprofData(String fileName, FileDescriptor fd,
+ String bitmapFormat) throws IOException {
+ try {
+ if (bitmapFormat != null) {
+ Bitmap.dumpAll(bitmapFormat);
+ }
+ VMDebug.dumpHprofData(fileName, fd);
+ } finally {
+ if (bitmapFormat != null) {
+ Bitmap.dumpAll(null); // clear dump data
+ }
+ }
+ }
+
+ /**
* Collect "hprof" and send it to DDMS. This may cause a GC.
*
* @throws UnsupportedOperationException if the VM was built without
diff --git a/core/java/android/os/EventLogTags.logtags b/core/java/android/os/EventLogTags.logtags
index b143a7443066..f57aad00e591 100644
--- a/core/java/android/os/EventLogTags.logtags
+++ b/core/java/android/os/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.os
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index d0828c384664..eaecd34b9d75 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import android.util.Printer;
@@ -839,6 +840,7 @@ public class Handler {
*@hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
@FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
public final void removeEqualMessages(int what, @Nullable Object object) {
mQueue.removeEqualMessages(this, what, disallowNullArgumentIfShared(object));
@@ -872,6 +874,7 @@ public class Handler {
*@hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
@FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
public final void removeCallbacksAndEqualMessages(@Nullable Object token) {
mQueue.removeCallbacksAndEqualMessages(this, disallowNullArgumentIfShared(token));
@@ -889,6 +892,7 @@ public class Handler {
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
@FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
public final boolean hasMessagesOrCallbacks() {
return mQueue.hasMessages(this);
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 4a14a8d0faf8..56a089aff78a 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -21,11 +21,13 @@ import android.os.CpuHeadroomParamsInternal;
import android.os.GpuHeadroomParamsInternal;
import android.os.IHintSession;
import android.os.SessionCreationConfig;
-import android.hardware.power.CpuHeadroomResult;
+
import android.hardware.power.ChannelConfig;
+import android.hardware.power.CpuHeadroomResult;
import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
+import android.hardware.power.SupportInfo;
/** {@hide} */
interface IHintManager {
@@ -40,11 +42,6 @@ interface IHintManager {
IHintSession createHintSessionWithConfig(in IBinder token, in SessionTag tag,
in SessionCreationConfig creationConfig, out SessionConfig config);
- /**
- * Get preferred rate limit in nanoseconds.
- */
- long getHintSessionPreferredRate();
-
void setHintSessionThreads(in IHintSession hintSession, in int[] tids);
int[] getHintSessionThreadIds(in IHintSession hintSession);
@@ -61,13 +58,28 @@ interface IHintManager {
long getGpuHeadroomMinIntervalMillis();
/**
- * Get Maximum number of graphics pipeline threads allowed per-app.
- */
- int getMaxGraphicsPipelineThreadsCount();
-
- /**
* Used by the JNI to pass an interface to the SessionManager;
* for internal use only.
*/
oneway void passSessionManagerBinder(in IBinder sessionManager);
+
+ parcelable HintManagerClientData {
+ int powerHalVersion;
+ int maxGraphicsPipelineThreads;
+ long preferredRateNanos;
+ SupportInfo supportInfo;
+ }
+
+ interface IHintManagerClient {
+ /**
+ * Returns FMQ channel information for the caller, which it associates to the callback binder lifespan.
+ */
+ oneway void receiveChannelConfig(in ChannelConfig config);
+ }
+
+ /**
+ * Set up an ADPF client, receiving a remote client binder interface and
+ * passing back a bundle of support and configuration information.
+ */
+ HintManagerClientData registerClient(in IHintManagerClient client);
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 8db1567336d3..a2e9314f6436 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -400,10 +400,11 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
}
/**
- * This is a convenience class that encapsulates configuration information for a
- * cache. It may be supplied to the cache constructors in lieu of the other
- * parameters. The class captures maximum entry count, the module, the key, and the
- * api.
+ * This is a convenience class that encapsulates configuration information for a cache. It
+ * may be supplied to the cache constructors in lieu of the other parameters. The class
+ * captures maximum entry count, the module, the key, and the api. The key is used to
+ * invalidate the cache and may be shared by different caches. The api is a user-visible (in
+ * debug) name for the cache.
*
* There are three specific use cases supported by this class.
*
@@ -430,11 +431,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* @hide
*/
public static class Config {
- private final int mMaxEntries;
- @IpcDataCacheModule
- private final String mModule;
- private final String mApi;
- private final String mName;
+ final Args mArgs;
+ final String mName;
/**
* The list of cache names that were created extending this Config. If
@@ -452,12 +450,20 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
*/
private boolean mDisabled = false;
+ /**
+ * Fully construct a config.
+ */
+ private Config(@NonNull Args args, @NonNull String name) {
+ mArgs = args;
+ mName = name;
+ }
+
+ /**
+ *
+ */
public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api, @NonNull String name) {
- mMaxEntries = maxEntries;
- mModule = module;
- mApi = api;
- mName = name;
+ this(new Args(module).api(api).maxEntries(maxEntries), name);
}
/**
@@ -473,7 +479,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
- this(root.maxEntries(), root.module(), api, name);
+ this(root.mArgs.api(api), name);
}
/**
@@ -481,7 +487,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api) {
- this(root.maxEntries(), root.module(), api, api);
+ this(root.mArgs.api(api), api);
}
/**
@@ -490,26 +496,23 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* current process.
*/
public Config child(@NonNull String name) {
- final Config result = new Config(this, api(), name);
+ final Config result = new Config(mArgs, name);
registerChild(name);
return result;
}
- public final int maxEntries() {
- return mMaxEntries;
- }
-
- @IpcDataCacheModule
- public final @NonNull String module() {
- return mModule;
- }
-
- public final @NonNull String api() {
- return mApi;
+ /**
+ * Set the cacheNull behavior.
+ */
+ public Config cacheNulls(boolean enable) {
+ return new Config(mArgs.cacheNulls(enable), mName);
}
- public final @NonNull String name() {
- return mName;
+ /**
+ * Set the isolateUidss behavior.
+ */
+ public Config isolateUids(boolean enable) {
+ return new Config(mArgs.isolateUids(enable), mName);
}
/**
@@ -532,7 +535,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* Invalidate all caches that share this Config's module and api.
*/
public void invalidateCache() {
- IpcDataCache.invalidateCache(mModule, mApi);
+ IpcDataCache.invalidateCache(mArgs);
}
/**
@@ -564,8 +567,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
- super(new Args(config.module()).maxEntries(config.maxEntries()).api(config.api()),
- config.name(), computer);
+ super(config.mArgs, config.mName, computer);
}
/**
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index cae82d010132..c0333e914b4d 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -16,9 +16,14 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.compat.annotation.UnsupportedAppUsage;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
@@ -99,6 +104,28 @@ public final class MessageQueue {
mPtr = nativeInit();
}
+ @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;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -713,6 +740,116 @@ public final class MessageQueue {
return true;
}
+ private Message legacyPeekOrPoll(boolean peek) {
+ synchronized (this) {
+ // Try to retrieve the next message. Return if found.
+ final long now = SystemClock.uptimeMillis();
+ Message prevMsg = null;
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ // Stalled by a barrier. Find the next asynchronous message in the queue.
+ do {
+ prevMsg = msg;
+ msg = msg.next;
+ } while (msg != null && !msg.isAsynchronous());
+ }
+ if (msg != null) {
+ if (now >= msg.when) {
+ // Got a message.
+ mBlocked = false;
+ if (peek) {
+ return msg;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
+ }
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
+ }
+ }
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret = legacyPeekOrPoll(true);
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ @Nullable
+ Message pollForTest() {
+ throwIfNotTest();
+ return legacyPeekOrPoll(false);
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ *
+ * Calls to this method must not be allowed to race with `next`.
+ * Specifically, the Looper thread must be paused before calling this method,
+ * and may not be resumed until after returning from this method.
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ Message iter = msg;
+ /* Look for a deliverable async node */
+ do {
+ iter = iter.next;
+ } while (iter != null && !iter.isAsynchronous());
+
+ long now = SystemClock.uptimeMillis();
+ if (iter != null && now >= iter.when) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ iter = msg;
+ do {
+ iter = iter.next;
+ } while (iter != null && (iter.target == null || iter.isAsynchronous()));
+
+ if (iter != null && now >= iter.when) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
boolean hasMessages(Handler h, int what, Object object) {
if (h == null) {
return false;
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ddf2b61324ad..012590510714 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -332,16 +332,55 @@ public final class Looper {
return -1;
}
+ private static int getThreadGroup() {
+ int threadGroup = Process.THREAD_GROUP_DEFAULT;
+
+ if (!Process.isIsolated()) {
+ threadGroup = Process.getProcessGroup(Process.myTid());
+ }
+ return threadGroup;
+ }
+
+ private static String threadGroupToString(int threadGroup) {
+ switch (threadGroup) {
+ case Process.THREAD_GROUP_BACKGROUND:
+ return "BACKGROUND";
+ case Process.THREAD_GROUP_FOREGROUND:
+ return "FOREGROUND";
+ case Process.THREAD_GROUP_SYSTEM:
+ return "SYSTEM";
+ case Process.THREAD_GROUP_AUDIO_APP:
+ return "AUDIO_APP";
+ case Process.THREAD_GROUP_AUDIO_SYS:
+ return "AUDIO_SYS";
+ case Process.THREAD_GROUP_TOP_APP:
+ return "TOP_APP";
+ case Process.THREAD_GROUP_RT_APP:
+ return "RT_APP";
+ case Process.THREAD_GROUP_RESTRICTED:
+ return "RESTRICTED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
String what, Message msg) {
final long actualTime = measureEnd - measureStart;
if (actualTime < threshold) {
return false;
}
+
+ String name = Process.myProcessName();
+ String threadGroup = threadGroupToString(getThreadGroup());
+ boolean isMain = myLooper() == getMainLooper();
+
// For slow delivery, the current message isn't really important, but log it anyway.
Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
- + Thread.currentThread().getName() + " h="
- + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
+ + Thread.currentThread().getName() + " app=" + name
+ + " main=" + isMain + " group=" + threadGroup
+ + " h=" + msg.target.getClass().getName() + " c=" + msg.callback
+ + " m=" + msg.what);
return true;
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index bfcc5cc6f18e..f3bb51490f20 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -115,12 +115,20 @@ per-file ProfilingServiceManager.java = file:/PERFORMANCE_OWNERS
# Performance
per-file IpcDataCache.java = file:/PERFORMANCE_OWNERS
+# Processes, threads, and scheduling
+per-file Process.java = file:/PERFORMANCE_OWNERS
+
# Memory
per-file OomKillRecord.java = file:/MEMORY_OWNERS
-# MessageQueue
+# MessageQueue and related classes
per-file MessageQueue.java = mfasheh@google.com, shayba@google.com
per-file Message.java = mfasheh@google.com, shayba@google.com
+per-file Looper.java = mfasheh@google.com, shayba@google.com
+per-file TestLooperManager.java = mfasheh@google.com, shayba@google.com
+per-file Handler.java = mfasheh@google.com, shayba@google.com
+per-file HandlerThread.java = mfasheh@google.com, shayba@google.com
+per-file HandlerExecutor.java = mfasheh@google.com, shayba@google.com
# Stats
per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6855d95d718f..cf473ec9c3ea 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -50,7 +50,6 @@ import com.android.internal.util.ArrayUtils;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
-import dalvik.annotation.optimization.NeverInline;
import libcore.util.SneakyThrow;
@@ -588,17 +587,6 @@ public final class Parcel {
return parcel;
}
- @NeverInline
- private void errorUsedWhileRecycling() {
- Log.wtf(TAG, "Parcel used while recycled. "
- + Log.getStackTraceString(new Throwable())
- + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
- }
-
- private void assertNotRecycled() {
- if (mRecycled) errorUsedWhileRecycling();
- }
-
/**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
@@ -647,7 +635,6 @@ public final class Parcel {
* @hide
*/
public void setReadWriteHelper(@Nullable ReadWriteHelper helper) {
- assertNotRecycled();
mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
}
@@ -657,7 +644,6 @@ public final class Parcel {
* @hide
*/
public boolean hasReadWriteHelper() {
- assertNotRecycled();
return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
}
@@ -684,7 +670,6 @@ public final class Parcel {
* @hide
*/
public final void markSensitive() {
- assertNotRecycled();
nativeMarkSensitive(mNativePtr);
}
@@ -701,7 +686,6 @@ public final class Parcel {
* @hide
*/
public final boolean isForRpc() {
- assertNotRecycled();
return nativeIsForRpc(mNativePtr);
}
@@ -709,25 +693,21 @@ public final class Parcel {
@ParcelFlags
@TestApi
public int getFlags() {
- assertNotRecycled();
return mFlags;
}
/** @hide */
public void setFlags(@ParcelFlags int flags) {
- assertNotRecycled();
mFlags = flags;
}
/** @hide */
public void addFlags(@ParcelFlags int flags) {
- assertNotRecycled();
mFlags |= flags;
}
/** @hide */
private boolean hasFlags(@ParcelFlags int flags) {
- assertNotRecycled();
return (mFlags & flags) == flags;
}
@@ -740,7 +720,6 @@ public final class Parcel {
// We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
// This would only work when used on a reply parcel by a binder object that's allowed-blocking.
public void setPropagateAllowBlocking() {
- assertNotRecycled();
addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
}
@@ -748,7 +727,6 @@ public final class Parcel {
* Returns the total amount of data contained in the parcel.
*/
public int dataSize() {
- assertNotRecycled();
return nativeDataSize(mNativePtr);
}
@@ -757,7 +735,6 @@ public final class Parcel {
* parcel. That is, {@link #dataSize}-{@link #dataPosition}.
*/
public final int dataAvail() {
- assertNotRecycled();
return nativeDataAvail(mNativePtr);
}
@@ -766,7 +743,6 @@ public final class Parcel {
* more than {@link #dataSize}.
*/
public final int dataPosition() {
- assertNotRecycled();
return nativeDataPosition(mNativePtr);
}
@@ -777,7 +753,6 @@ public final class Parcel {
* data buffer.
*/
public final int dataCapacity() {
- assertNotRecycled();
return nativeDataCapacity(mNativePtr);
}
@@ -789,7 +764,6 @@ public final class Parcel {
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- assertNotRecycled();
nativeSetDataSize(mNativePtr, size);
}
@@ -799,7 +773,6 @@ public final class Parcel {
* {@link #dataSize}.
*/
public final void setDataPosition(int pos) {
- assertNotRecycled();
nativeSetDataPosition(mNativePtr, pos);
}
@@ -811,13 +784,11 @@ public final class Parcel {
* with this method.
*/
public final void setDataCapacity(int size) {
- assertNotRecycled();
nativeSetDataCapacity(mNativePtr, size);
}
/** @hide */
public final boolean pushAllowFds(boolean allowFds) {
- assertNotRecycled();
return nativePushAllowFds(mNativePtr, allowFds);
}
@@ -838,7 +809,6 @@ public final class Parcel {
* in different versions of the platform.
*/
public final byte[] marshall() {
- assertNotRecycled();
return nativeMarshall(mNativePtr);
}
@@ -846,18 +816,15 @@ public final class Parcel {
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
- assertNotRecycled();
nativeUnmarshall(mNativePtr, data, offset, length);
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- assertNotRecycled();
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
/** @hide */
public int compareData(Parcel other) {
- assertNotRecycled();
return nativeCompareData(mNativePtr, other.mNativePtr);
}
@@ -868,7 +835,6 @@ public final class Parcel {
/** @hide */
public final void setClassCookie(Class clz, Object cookie) {
- assertNotRecycled();
if (mClassCookies == null) {
mClassCookies = new ArrayMap<>();
}
@@ -878,13 +844,11 @@ public final class Parcel {
/** @hide */
@Nullable
public final Object getClassCookie(Class clz) {
- assertNotRecycled();
return mClassCookies != null ? mClassCookies.get(clz) : null;
}
/** @hide */
public void removeClassCookie(Class clz, Object expectedCookie) {
- assertNotRecycled();
if (mClassCookies != null) {
Object removedCookie = mClassCookies.remove(clz);
if (removedCookie != expectedCookie) {
@@ -902,25 +866,21 @@ public final class Parcel {
* @hide
*/
public boolean hasClassCookie(Class clz) {
- assertNotRecycled();
return mClassCookies != null && mClassCookies.containsKey(clz);
}
/** @hide */
public final void adoptClassCookies(Parcel from) {
- assertNotRecycled();
mClassCookies = from.mClassCookies;
}
/** @hide */
public Map<Class, Object> copyClassCookies() {
- assertNotRecycled();
return new ArrayMap<>(mClassCookies);
}
/** @hide */
public void putClassCookies(Map<Class, Object> cookies) {
- assertNotRecycled();
if (cookies == null) {
return;
}
@@ -932,9 +892,14 @@ public final class Parcel {
/**
* Report whether the parcel contains any marshalled file descriptors.
+ *
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
*/
public boolean hasFileDescriptors() {
- assertNotRecycled();
return nativeHasFileDescriptors(mNativePtr);
}
@@ -942,6 +907,12 @@ public final class Parcel {
* Report whether the parcel contains any marshalled file descriptors in the range defined by
* {@code offset} and {@code length}.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @param offset The offset from which the range starts. Should be between 0 and
* {@link #dataSize()}.
* @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code
@@ -950,7 +921,6 @@ public final class Parcel {
* @throws IllegalArgumentException if the parameters are out of the permitted ranges.
*/
public boolean hasFileDescriptors(int offset, int length) {
- assertNotRecycled();
return nativeHasFileDescriptorsInRange(mNativePtr, offset, length);
}
@@ -963,6 +933,12 @@ public final class Parcel {
* <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
* for that.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @throws IllegalArgumentException if you provide any object not supported by above methods
* (including if the unsupported object is inside a nested container).
*
@@ -1032,10 +1008,16 @@ public final class Parcel {
*
* @throws UnsupportedOperationException if binder kernel driver was disabled or if method was
* invoked in case of Binder RPC protocol.
+ *
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @hide
*/
public boolean hasBinders() {
- assertNotRecycled();
return nativeHasBinders(mNativePtr);
}
@@ -1043,6 +1025,12 @@ public final class Parcel {
* Report whether the parcel contains any marshalled {@link IBinder} objects in the range
* defined by {@code offset} and {@code length}.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @param offset The offset from which the range starts. Should be between 0 and
* {@link #dataSize()}.
* @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code
@@ -1053,7 +1041,6 @@ public final class Parcel {
* @hide
*/
public boolean hasBinders(int offset, int length) {
- assertNotRecycled();
return nativeHasBindersInRange(mNativePtr, offset, length);
}
@@ -1064,7 +1051,6 @@ public final class Parcel {
* at the beginning of transactions as a header.
*/
public final void writeInterfaceToken(@NonNull String interfaceName) {
- assertNotRecycled();
nativeWriteInterfaceToken(mNativePtr, interfaceName);
}
@@ -1075,7 +1061,6 @@ public final class Parcel {
* should propagate to the caller.
*/
public final void enforceInterface(@NonNull String interfaceName) {
- assertNotRecycled();
nativeEnforceInterface(mNativePtr, interfaceName);
}
@@ -1086,7 +1071,6 @@ public final class Parcel {
* When used over binder, this exception should propagate to the caller.
*/
public void enforceNoDataAvail() {
- assertNotRecycled();
final int n = dataAvail();
if (n > 0) {
throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n);
@@ -1103,7 +1087,6 @@ public final class Parcel {
* @hide
*/
public boolean replaceCallingWorkSourceUid(int workSourceUid) {
- assertNotRecycled();
return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid);
}
@@ -1120,7 +1103,6 @@ public final class Parcel {
* @hide
*/
public int readCallingWorkSourceUid() {
- assertNotRecycled();
return nativeReadCallingWorkSourceUid(mNativePtr);
}
@@ -1130,7 +1112,6 @@ public final class Parcel {
* @param b Bytes to place into the parcel.
*/
public final void writeByteArray(@Nullable byte[] b) {
- assertNotRecycled();
writeByteArray(b, 0, (b != null) ? b.length : 0);
}
@@ -1142,7 +1123,6 @@ public final class Parcel {
* @param len Number of bytes to write.
*/
public final void writeByteArray(@Nullable byte[] b, int offset, int len) {
- assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1164,7 +1144,6 @@ public final class Parcel {
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b) {
- assertNotRecycled();
writeBlob(b, 0, (b != null) ? b.length : 0);
}
@@ -1183,7 +1162,6 @@ public final class Parcel {
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b, int offset, int len) {
- assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1202,7 +1180,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
- assertNotRecycled();
int err = nativeWriteInt(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1214,7 +1191,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
- assertNotRecycled();
int err = nativeWriteLong(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1226,7 +1202,6 @@ public final class Parcel {
* dataPosition(), growing dataCapacity() if needed.
*/
public final void writeFloat(float val) {
- assertNotRecycled();
int err = nativeWriteFloat(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1238,7 +1213,6 @@ public final class Parcel {
* current dataPosition(), growing dataCapacity() if needed.
*/
public final void writeDouble(double val) {
- assertNotRecycled();
int err = nativeWriteDouble(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1250,19 +1224,16 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeString(@Nullable String val) {
- assertNotRecycled();
writeString16(val);
}
/** {@hide} */
public final void writeString8(@Nullable String val) {
- assertNotRecycled();
mReadWriteHelper.writeString8(this, val);
}
/** {@hide} */
public final void writeString16(@Nullable String val) {
- assertNotRecycled();
mReadWriteHelper.writeString16(this, val);
}
@@ -1274,19 +1245,16 @@ public final class Parcel {
* @hide
*/
public void writeStringNoHelper(@Nullable String val) {
- assertNotRecycled();
writeString16NoHelper(val);
}
/** {@hide} */
public void writeString8NoHelper(@Nullable String val) {
- assertNotRecycled();
nativeWriteString8(mNativePtr, val);
}
/** {@hide} */
public void writeString16NoHelper(@Nullable String val) {
- assertNotRecycled();
nativeWriteString16(mNativePtr, val);
}
@@ -1298,7 +1266,6 @@ public final class Parcel {
* for true or false, respectively, but may change in the future.
*/
public final void writeBoolean(boolean val) {
- assertNotRecycled();
writeInt(val ? 1 : 0);
}
@@ -1310,7 +1277,6 @@ public final class Parcel {
@UnsupportedAppUsage
@RavenwoodThrow(blockedBy = android.text.Spanned.class)
public final void writeCharSequence(@Nullable CharSequence val) {
- assertNotRecycled();
TextUtils.writeToParcel(val, this, 0);
}
@@ -1319,7 +1285,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeStrongBinder(IBinder val) {
- assertNotRecycled();
nativeWriteStrongBinder(mNativePtr, val);
}
@@ -1328,7 +1293,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeStrongInterface(IInterface val) {
- assertNotRecycled();
writeStrongBinder(val == null ? null : val.asBinder());
}
@@ -1343,7 +1307,6 @@ public final class Parcel {
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(@NonNull FileDescriptor val) {
- assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1352,7 +1315,6 @@ public final class Parcel {
* This will be the new name for writeFileDescriptor, for consistency.
**/
public final void writeRawFileDescriptor(@NonNull FileDescriptor val) {
- assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1363,7 +1325,6 @@ public final class Parcel {
* @param value The array of objects to be written.
*/
public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) {
- assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -1383,7 +1344,6 @@ public final class Parcel {
* the future.
*/
public final void writeByte(byte val) {
- assertNotRecycled();
writeInt(val);
}
@@ -1399,7 +1359,6 @@ public final class Parcel {
* allows you to avoid mysterious type errors at the point of marshalling.
*/
public final void writeMap(@Nullable Map val) {
- assertNotRecycled();
writeMapInternal((Map<String, Object>) val);
}
@@ -1408,7 +1367,6 @@ public final class Parcel {
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeMapInternal(@Nullable Map<String,Object> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1434,7 +1392,6 @@ public final class Parcel {
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1464,7 +1421,6 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArrayMap(@Nullable ArrayMap<String, Object> val) {
- assertNotRecycled();
writeArrayMapInternal(val);
}
@@ -1483,7 +1439,6 @@ public final class Parcel {
*/
public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1505,7 +1460,6 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
- assertNotRecycled();
final int size = (val != null) ? val.size() : -1;
writeInt(size);
for (int i = 0; i < size; i++) {
@@ -1518,7 +1472,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeBundle(@Nullable Bundle val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1532,7 +1485,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writePersistableBundle(@Nullable PersistableBundle val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1546,7 +1498,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeSize(@NonNull Size val) {
- assertNotRecycled();
writeInt(val.getWidth());
writeInt(val.getHeight());
}
@@ -1556,7 +1507,6 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeSizeF(@NonNull SizeF val) {
- assertNotRecycled();
writeFloat(val.getWidth());
writeFloat(val.getHeight());
}
@@ -1567,7 +1517,6 @@ public final class Parcel {
* {@link #writeValue} and must follow the specification there.
*/
public final void writeList(@Nullable List val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1587,7 +1536,6 @@ public final class Parcel {
* {@link #writeValue} and must follow the specification there.
*/
public final void writeArray(@Nullable Object[] val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1608,7 +1556,6 @@ public final class Parcel {
* specification there.
*/
public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1624,7 +1571,6 @@ public final class Parcel {
}
public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1643,7 +1589,6 @@ public final class Parcel {
* @hide
*/
public final void writeSparseIntArray(@Nullable SparseIntArray val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1659,7 +1604,6 @@ public final class Parcel {
}
public final void writeBooleanArray(@Nullable boolean[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1694,7 +1638,6 @@ public final class Parcel {
}
private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) {
- assertNotRecycled();
// For Multidimensional arrays, Calculate total object
// which will be allocated.
int totalObjects = 1;
@@ -1712,7 +1655,6 @@ public final class Parcel {
}
private void ensureWithinMemoryLimit(int typeSize, int length) {
- assertNotRecycled();
int estimatedAllocationSize = 0;
try {
estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -1736,7 +1678,6 @@ public final class Parcel {
@Nullable
public final boolean[] createBooleanArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_BOOLEAN, N);
// >>2 as a fast divide-by-4 works in the create*Array() functions
@@ -1754,7 +1695,6 @@ public final class Parcel {
}
public final void readBooleanArray(@NonNull boolean[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1767,7 +1707,6 @@ public final class Parcel {
/** @hide */
public void writeShortArray(@Nullable short[] val) {
- assertNotRecycled();
if (val != null) {
int n = val.length;
writeInt(n);
@@ -1782,7 +1721,6 @@ public final class Parcel {
/** @hide */
@Nullable
public short[] createShortArray() {
- assertNotRecycled();
int n = readInt();
ensureWithinMemoryLimit(SIZE_SHORT, n);
if (n >= 0 && n <= (dataAvail() >> 2)) {
@@ -1798,7 +1736,6 @@ public final class Parcel {
/** @hide */
public void readShortArray(@NonNull short[] val) {
- assertNotRecycled();
int n = readInt();
if (n == val.length) {
for (int i = 0; i < n; i++) {
@@ -1810,7 +1747,6 @@ public final class Parcel {
}
public final void writeCharArray(@Nullable char[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1824,7 +1760,6 @@ public final class Parcel {
@Nullable
public final char[] createCharArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_CHAR, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1839,7 +1774,6 @@ public final class Parcel {
}
public final void readCharArray(@NonNull char[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1851,7 +1785,6 @@ public final class Parcel {
}
public final void writeIntArray(@Nullable int[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1865,7 +1798,6 @@ public final class Parcel {
@Nullable
public final int[] createIntArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_INT, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1880,7 +1812,6 @@ public final class Parcel {
}
public final void readIntArray(@NonNull int[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1892,7 +1823,6 @@ public final class Parcel {
}
public final void writeLongArray(@Nullable long[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1906,7 +1836,6 @@ public final class Parcel {
@Nullable
public final long[] createLongArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_LONG, N);
// >>3 because stored longs are 64 bits
@@ -1922,7 +1851,6 @@ public final class Parcel {
}
public final void readLongArray(@NonNull long[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1934,7 +1862,6 @@ public final class Parcel {
}
public final void writeFloatArray(@Nullable float[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1948,7 +1875,6 @@ public final class Parcel {
@Nullable
public final float[] createFloatArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_FLOAT, N);
// >>2 because stored floats are 4 bytes
@@ -1964,7 +1890,6 @@ public final class Parcel {
}
public final void readFloatArray(@NonNull float[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1976,7 +1901,6 @@ public final class Parcel {
}
public final void writeDoubleArray(@Nullable double[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1990,7 +1914,6 @@ public final class Parcel {
@Nullable
public final double[] createDoubleArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_DOUBLE, N);
// >>3 because stored doubles are 8 bytes
@@ -2006,7 +1929,6 @@ public final class Parcel {
}
public final void readDoubleArray(@NonNull double[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2018,24 +1940,20 @@ public final class Parcel {
}
public final void writeStringArray(@Nullable String[] val) {
- assertNotRecycled();
writeString16Array(val);
}
@Nullable
public final String[] createStringArray() {
- assertNotRecycled();
return createString16Array();
}
public final void readStringArray(@NonNull String[] val) {
- assertNotRecycled();
readString16Array(val);
}
/** {@hide} */
public final void writeString8Array(@Nullable String[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2050,7 +1968,6 @@ public final class Parcel {
/** {@hide} */
@Nullable
public final String[] createString8Array() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2066,7 +1983,6 @@ public final class Parcel {
/** {@hide} */
public final void readString8Array(@NonNull String[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2079,7 +1995,6 @@ public final class Parcel {
/** {@hide} */
public final void writeString16Array(@Nullable String[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2094,7 +2009,6 @@ public final class Parcel {
/** {@hide} */
@Nullable
public final String[] createString16Array() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2110,7 +2024,6 @@ public final class Parcel {
/** {@hide} */
public final void readString16Array(@NonNull String[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2122,7 +2035,6 @@ public final class Parcel {
}
public final void writeBinderArray(@Nullable IBinder[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2147,7 +2059,6 @@ public final class Parcel {
*/
public final <T extends IInterface> void writeInterfaceArray(
@SuppressLint("ArrayReturn") @Nullable T[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2163,7 +2074,6 @@ public final class Parcel {
* @hide
*/
public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2179,7 +2089,6 @@ public final class Parcel {
* @hide
*/
public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) {
- assertNotRecycled();
if (val != null) {
int N = val.size();
writeInt(N);
@@ -2193,7 +2102,6 @@ public final class Parcel {
@Nullable
public final IBinder[] createBinderArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2208,7 +2116,6 @@ public final class Parcel {
}
public final void readBinderArray(@NonNull IBinder[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2230,7 +2137,6 @@ public final class Parcel {
@Nullable
public final <T extends IInterface> T[] createInterfaceArray(
@NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2255,7 +2161,6 @@ public final class Parcel {
public final <T extends IInterface> void readInterfaceArray(
@SuppressLint("ArrayReturn") @NonNull T[] val,
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2281,7 +2186,6 @@ public final class Parcel {
* @see Parcelable
*/
public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) {
- assertNotRecycled();
writeTypedList(val, 0);
}
@@ -2301,7 +2205,6 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2331,7 +2234,6 @@ public final class Parcel {
* @see Parcelable
*/
public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2357,7 +2259,6 @@ public final class Parcel {
* @see #readStringList
*/
public final void writeStringList(@Nullable List<String> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2383,7 +2284,6 @@ public final class Parcel {
* @see #readBinderList
*/
public final void writeBinderList(@Nullable List<IBinder> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2406,7 +2306,6 @@ public final class Parcel {
* @see #readInterfaceList
*/
public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2428,7 +2327,6 @@ public final class Parcel {
* @see #readParcelableList(List, ClassLoader)
*/
public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2463,7 +2361,6 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val,
int parcelableFlags) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2486,7 +2383,6 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedObject(@Nullable T val,
int parcelableFlags) {
- assertNotRecycled();
if (val != null) {
writeInt(1);
val.writeToParcel(this, parcelableFlags);
@@ -2524,7 +2420,6 @@ public final class Parcel {
*/
public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
@NonNull int... dimensions) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2636,7 +2531,6 @@ public final class Parcel {
* should be used).</p>
*/
public final void writeValue(@Nullable Object v) {
- assertNotRecycled();
if (v instanceof LazyValue) {
LazyValue value = (LazyValue) v;
value.writeToParcel(this);
@@ -2754,7 +2648,6 @@ public final class Parcel {
* @hide
*/
public void writeValue(int type, @Nullable Object v) {
- assertNotRecycled();
switch (type) {
case VAL_NULL:
break;
@@ -2868,7 +2761,6 @@ public final class Parcel {
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
*/
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
- assertNotRecycled();
if (p == null) {
writeString(null);
return;
@@ -2884,7 +2776,6 @@ public final class Parcel {
* @see #readParcelableCreator
*/
public final void writeParcelableCreator(@NonNull Parcelable p) {
- assertNotRecycled();
String name = p.getClass().getName();
writeString(name);
}
@@ -2923,7 +2814,6 @@ public final class Parcel {
*/
@TestApi
public boolean allowSquashing() {
- assertNotRecycled();
boolean previous = mAllowSquashing;
mAllowSquashing = true;
return previous;
@@ -2935,7 +2825,6 @@ public final class Parcel {
*/
@TestApi
public void restoreAllowSquashing(boolean previous) {
- assertNotRecycled();
mAllowSquashing = previous;
if (!mAllowSquashing) {
mWrittenSquashableParcelables = null;
@@ -2992,7 +2881,6 @@ public final class Parcel {
* @hide
*/
public boolean maybeWriteSquashed(@NonNull Parcelable p) {
- assertNotRecycled();
if (!mAllowSquashing) {
// Don't squash, and don't put it in the map either.
writeInt(0);
@@ -3043,7 +2931,6 @@ public final class Parcel {
@SuppressWarnings("unchecked")
@Nullable
public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
- assertNotRecycled();
final int offset = readInt();
final int pos = dataPosition();
@@ -3077,7 +2964,6 @@ public final class Parcel {
* using the other approaches to writing data in to a Parcel.
*/
public final void writeSerializable(@Nullable Serializable s) {
- assertNotRecycled();
if (s == null) {
writeString(null);
return;
@@ -3130,7 +3016,6 @@ public final class Parcel {
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeException(@NonNull Exception e) {
- assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
int code = getExceptionCode(e);
@@ -3211,7 +3096,6 @@ public final class Parcel {
/** @hide */
public void writeStackTrace(@NonNull Throwable e) {
- assertNotRecycled();
final int sizePosition = dataPosition();
writeInt(0); // Header size will be filled in later
StackTraceElement[] stackTrace = e.getStackTrace();
@@ -3237,7 +3121,6 @@ public final class Parcel {
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeNoException() {
- assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
// Despite the name of this function ("write no exception"),
@@ -3281,7 +3164,6 @@ public final class Parcel {
* @see #writeNoException
*/
public final void readException() {
- assertNotRecycled();
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
@@ -3305,7 +3187,6 @@ public final class Parcel {
@UnsupportedAppUsage
@TestApi
public final int readExceptionCode() {
- assertNotRecycled();
int code = readInt();
if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
AppOpsManager.readAndLogNotedAppops(this);
@@ -3339,7 +3220,6 @@ public final class Parcel {
* @param msg The exception message.
*/
public final void readException(int code, String msg) {
- assertNotRecycled();
String remoteStackTrace = null;
final int remoteStackPayloadSize = readInt();
if (remoteStackPayloadSize > 0) {
@@ -3370,7 +3250,6 @@ public final class Parcel {
/** @hide */
public Exception createExceptionOrNull(int code, String msg) {
- assertNotRecycled();
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
@@ -3403,7 +3282,6 @@ public final class Parcel {
* Read an integer value from the parcel at the current dataPosition().
*/
public final int readInt() {
- assertNotRecycled();
return nativeReadInt(mNativePtr);
}
@@ -3411,7 +3289,6 @@ public final class Parcel {
* Read a long integer value from the parcel at the current dataPosition().
*/
public final long readLong() {
- assertNotRecycled();
return nativeReadLong(mNativePtr);
}
@@ -3420,7 +3297,6 @@ public final class Parcel {
* dataPosition().
*/
public final float readFloat() {
- assertNotRecycled();
return nativeReadFloat(mNativePtr);
}
@@ -3429,7 +3305,6 @@ public final class Parcel {
* current dataPosition().
*/
public final double readDouble() {
- assertNotRecycled();
return nativeReadDouble(mNativePtr);
}
@@ -3438,19 +3313,16 @@ public final class Parcel {
*/
@Nullable
public final String readString() {
- assertNotRecycled();
return readString16();
}
/** {@hide} */
public final @Nullable String readString8() {
- assertNotRecycled();
return mReadWriteHelper.readString8(this);
}
/** {@hide} */
public final @Nullable String readString16() {
- assertNotRecycled();
return mReadWriteHelper.readString16(this);
}
@@ -3462,19 +3334,16 @@ public final class Parcel {
* @hide
*/
public @Nullable String readStringNoHelper() {
- assertNotRecycled();
return readString16NoHelper();
}
/** {@hide} */
public @Nullable String readString8NoHelper() {
- assertNotRecycled();
return nativeReadString8(mNativePtr);
}
/** {@hide} */
public @Nullable String readString16NoHelper() {
- assertNotRecycled();
return nativeReadString16(mNativePtr);
}
@@ -3482,7 +3351,6 @@ public final class Parcel {
* Read a boolean value from the parcel at the current dataPosition().
*/
public final boolean readBoolean() {
- assertNotRecycled();
return readInt() != 0;
}
@@ -3493,7 +3361,6 @@ public final class Parcel {
@UnsupportedAppUsage
@Nullable
public final CharSequence readCharSequence() {
- assertNotRecycled();
return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
}
@@ -3501,7 +3368,6 @@ public final class Parcel {
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
- assertNotRecycled();
final IBinder result = nativeReadStrongBinder(mNativePtr);
// If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
@@ -3517,7 +3383,6 @@ public final class Parcel {
* Read a FileDescriptor from the parcel at the current dataPosition().
*/
public final ParcelFileDescriptor readFileDescriptor() {
- assertNotRecycled();
FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
@@ -3525,7 +3390,6 @@ public final class Parcel {
/** {@hide} */
@UnsupportedAppUsage
public final FileDescriptor readRawFileDescriptor() {
- assertNotRecycled();
return nativeReadFileDescriptor(mNativePtr);
}
@@ -3536,7 +3400,6 @@ public final class Parcel {
**/
@Nullable
public final FileDescriptor[] createRawFileDescriptorArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3556,7 +3419,6 @@ public final class Parcel {
* @return the FileDescriptor array, or null if the array is null.
**/
public final void readRawFileDescriptorArray(FileDescriptor[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -3571,7 +3433,6 @@ public final class Parcel {
* Read a byte value from the parcel at the current dataPosition().
*/
public final byte readByte() {
- assertNotRecycled();
return (byte)(readInt() & 0xff);
}
@@ -3586,7 +3447,6 @@ public final class Parcel {
*/
@Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
- assertNotRecycled();
readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3600,7 +3460,6 @@ public final class Parcel {
public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
@Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
@NonNull Class<V> clazzValue) {
- assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
readMapInternal(outVal, loader, clazzKey, clazzValue);
@@ -3619,7 +3478,6 @@ public final class Parcel {
*/
@Deprecated
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
- assertNotRecycled();
int N = readInt();
readListInternal(outVal, N, loader, /* clazz */ null);
}
@@ -3641,7 +3499,6 @@ public final class Parcel {
*/
public <T> void readList(@NonNull List<? super T> outVal,
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
int n = readInt();
readListInternal(outVal, n, loader, clazz);
@@ -3661,7 +3518,6 @@ public final class Parcel {
@Deprecated
@Nullable
public HashMap readHashMap(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3676,7 +3532,6 @@ public final class Parcel {
@Nullable
public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
- assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
return readHashMapInternal(loader, clazzKey, clazzValue);
@@ -3689,7 +3544,6 @@ public final class Parcel {
*/
@Nullable
public final Bundle readBundle() {
- assertNotRecycled();
return readBundle(null);
}
@@ -3701,7 +3555,6 @@ public final class Parcel {
*/
@Nullable
public final Bundle readBundle(@Nullable ClassLoader loader) {
- assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3722,7 +3575,6 @@ public final class Parcel {
*/
@Nullable
public final PersistableBundle readPersistableBundle() {
- assertNotRecycled();
return readPersistableBundle(null);
}
@@ -3734,7 +3586,6 @@ public final class Parcel {
*/
@Nullable
public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) {
- assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3753,7 +3604,6 @@ public final class Parcel {
*/
@NonNull
public final Size readSize() {
- assertNotRecycled();
final int width = readInt();
final int height = readInt();
return new Size(width, height);
@@ -3764,7 +3614,6 @@ public final class Parcel {
*/
@NonNull
public final SizeF readSizeF() {
- assertNotRecycled();
final float width = readFloat();
final float height = readFloat();
return new SizeF(width, height);
@@ -3775,7 +3624,6 @@ public final class Parcel {
*/
@Nullable
public final byte[] createByteArray() {
- assertNotRecycled();
return nativeCreateByteArray(mNativePtr);
}
@@ -3784,7 +3632,6 @@ public final class Parcel {
* given byte array.
*/
public final void readByteArray(@NonNull byte[] val) {
- assertNotRecycled();
boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0);
if (!valid) {
throw new RuntimeException("bad array lengths");
@@ -3797,7 +3644,6 @@ public final class Parcel {
*/
@Nullable
public final byte[] readBlob() {
- assertNotRecycled();
return nativeReadBlob(mNativePtr);
}
@@ -3808,7 +3654,6 @@ public final class Parcel {
@UnsupportedAppUsage
@Nullable
public final String[] readStringArray() {
- assertNotRecycled();
return createString16Array();
}
@@ -3818,7 +3663,6 @@ public final class Parcel {
*/
@Nullable
public final CharSequence[] readCharSequenceArray() {
- assertNotRecycled();
CharSequence[] array = null;
int length = readInt();
@@ -3841,7 +3685,6 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<CharSequence> readCharSequenceList() {
- assertNotRecycled();
ArrayList<CharSequence> array = null;
int length = readInt();
@@ -3871,7 +3714,6 @@ public final class Parcel {
@Deprecated
@Nullable
public ArrayList readArrayList(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readArrayListInternal(loader, /* clazz */ null);
}
@@ -3894,7 +3736,6 @@ public final class Parcel {
@Nullable
public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayListInternal(loader, clazz);
}
@@ -3914,7 +3755,6 @@ public final class Parcel {
@Deprecated
@Nullable
public Object[] readArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readArrayInternal(loader, /* clazz */ null);
}
@@ -3936,7 +3776,6 @@ public final class Parcel {
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayInternal(loader, clazz);
}
@@ -3956,7 +3795,6 @@ public final class Parcel {
@Deprecated
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readSparseArrayInternal(loader, /* clazz */ null);
}
@@ -3978,7 +3816,6 @@ public final class Parcel {
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readSparseArrayInternal(loader, clazz);
}
@@ -3990,7 +3827,6 @@ public final class Parcel {
*/
@Nullable
public final SparseBooleanArray readSparseBooleanArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4007,7 +3843,6 @@ public final class Parcel {
*/
@Nullable
public final SparseIntArray readSparseIntArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4032,7 +3867,6 @@ public final class Parcel {
*/
@Nullable
public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4056,7 +3890,6 @@ public final class Parcel {
* @see #writeTypedList
*/
public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4086,7 +3919,6 @@ public final class Parcel {
*/
public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
@NonNull Parcelable.Creator<T> creator) {
- assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -4116,7 +3948,6 @@ public final class Parcel {
*/
public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
@NonNull Parcelable.Creator<T> creator) {
- assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -4144,7 +3975,6 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<String> createStringArrayList() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4171,7 +4001,6 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<IBinder> createBinderArrayList() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4199,7 +4028,6 @@ public final class Parcel {
@Nullable
public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4220,7 +4048,6 @@ public final class Parcel {
* @see #writeStringList
*/
public final void readStringList(@NonNull List<String> list) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4242,7 +4069,6 @@ public final class Parcel {
* @see #writeBinderList
*/
public final void readBinderList(@NonNull List<IBinder> list) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4265,7 +4091,6 @@ public final class Parcel {
*/
public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4297,7 +4122,6 @@ public final class Parcel {
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
- assertNotRecycled();
return readParcelableListInternal(list, cl, /*clazz*/ null);
}
@@ -4319,7 +4143,6 @@ public final class Parcel {
@NonNull
public <T> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(list);
Objects.requireNonNull(clazz);
return readParcelableListInternal(list, cl, clazz);
@@ -4365,7 +4188,6 @@ public final class Parcel {
*/
@Nullable
public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4379,7 +4201,6 @@ public final class Parcel {
}
public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -4396,7 +4217,6 @@ public final class Parcel {
*/
@Deprecated
public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
- assertNotRecycled();
return createTypedArray(c);
}
@@ -4413,7 +4233,6 @@ public final class Parcel {
*/
@Nullable
public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
if (readInt() != 0) {
return c.createFromParcel(this);
} else {
@@ -4440,7 +4259,6 @@ public final class Parcel {
* @see #readTypedArray
*/
public <T> void readFixedArray(@NonNull T val) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (componentType == boolean.class) {
readBooleanArray((boolean[]) val);
@@ -4481,7 +4299,6 @@ public final class Parcel {
*/
public <T, S extends IInterface> void readFixedArray(@NonNull T val,
@NonNull Function<IBinder, S> asInterface) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (IInterface.class.isAssignableFrom(componentType)) {
readInterfaceArray((S[]) val, asInterface);
@@ -4508,7 +4325,6 @@ public final class Parcel {
*/
public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
@NonNull Parcelable.Creator<S> c) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
readTypedArray((S[]) val, c);
@@ -4566,7 +4382,6 @@ public final class Parcel {
*/
@Nullable
public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4640,7 +4455,6 @@ public final class Parcel {
@Nullable
public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
@NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4701,7 +4515,6 @@ public final class Parcel {
@Nullable
public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
@NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4765,7 +4578,6 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value,
int parcelableFlags) {
- assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -4784,7 +4596,6 @@ public final class Parcel {
*/
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readValue(loader, /* clazz */ null);
}
@@ -4840,7 +4651,6 @@ public final class Parcel {
*/
@Nullable
public Object readLazyValue(@Nullable ClassLoader loader) {
- assertNotRecycled();
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -5243,7 +5053,6 @@ public final class Parcel {
@Deprecated
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableInternal(loader, /* clazz */ null);
}
@@ -5263,7 +5072,6 @@ public final class Parcel {
*/
@Nullable
public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
@@ -5292,7 +5100,6 @@ public final class Parcel {
@Nullable
public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator,
@Nullable ClassLoader loader) {
- assertNotRecycled();
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
@@ -5320,7 +5127,6 @@ public final class Parcel {
@Deprecated
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableCreatorInternal(loader, /* clazz */ null);
}
@@ -5341,7 +5147,6 @@ public final class Parcel {
@Nullable
public <T> Parcelable.Creator<T> readParcelableCreator(
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableCreatorInternal(loader, clazz);
}
@@ -5464,7 +5269,6 @@ public final class Parcel {
@Deprecated
@Nullable
public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableArrayInternal(loader, /* clazz */ null);
}
@@ -5485,7 +5289,6 @@ public final class Parcel {
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
return readParcelableArrayInternal(loader, requireNonNull(clazz));
}
@@ -5519,7 +5322,6 @@ public final class Parcel {
@Deprecated
@Nullable
public Serializable readSerializable() {
- assertNotRecycled();
return readSerializableInternal(/* loader */ null, /* clazz */ null);
}
@@ -5536,7 +5338,6 @@ public final class Parcel {
*/
@Nullable
public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readSerializableInternal(
loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -5778,7 +5579,6 @@ public final class Parcel {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
@Nullable ClassLoader loader) {
- assertNotRecycled();
final int N = readInt();
if (N < 0) {
return;
@@ -5795,7 +5595,6 @@ public final class Parcel {
*/
@UnsupportedAppUsage
public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) {
- assertNotRecycled();
final int size = readInt();
if (size < 0) {
return null;
@@ -5935,7 +5734,6 @@ public final class Parcel {
* @hide For testing
*/
public long getOpenAshmemSize() {
- assertNotRecycled();
return nativeGetOpenAshmemSize(mNativePtr);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e7282435ad46..907d96834857 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -553,10 +553,9 @@ public class Process {
* Foreground thread group - All threads in
* this group are scheduled with a normal share of the CPU.
* Value is same as constant SP_FOREGROUND of enum SchedPolicy.
- * Not used at this level.
* @hide
**/
- private static final int THREAD_GROUP_FOREGROUND = 1;
+ public static final int THREAD_GROUP_FOREGROUND = 1;
/**
* System thread group.
@@ -1315,19 +1314,6 @@ public class Process {
}
/**
- * Adjust the swappiness level for a process.
- *
- * @param pid The process identifier to set.
- * @param is_increased Whether swappiness should be increased or default.
- *
- * @return Returns true if the underlying system supports this
- * feature, else false.
- *
- * {@hide}
- */
- public static final native boolean setSwappiness(int pid, boolean is_increased);
-
- /**
* Change this process's argv[0] parameter. This can be useful to show
* more descriptive information in things like the 'ps' command.
*
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 8aec7eb59e91..9085fe09bdaa 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,8 @@ public final class ServiceManager {
if (service != null) {
return service;
} else {
- return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
+ return Binder.allowBlocking(
+ getIServiceManager().checkService(name).getServiceWithMetadata().service);
}
} catch (RemoteException e) {
Log.e(TAG, "error in checkService", e);
@@ -425,7 +426,8 @@ public final class ServiceManager {
private static IBinder rawGetService(String name) throws RemoteException {
final long start = sStatLogger.getTime();
- final IBinder binder = getIServiceManager().getService2(name).getBinder();
+ final IBinder binder =
+ getIServiceManager().getService2(name).getServiceWithMetadata().service;
final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 5a9c8787ee3b..7ea521ec5dd4 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -50,7 +50,8 @@ public final class ServiceManagerNative {
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
- mServiceManager = IServiceManager.Stub.asInterface(this.getNativeServiceManager());
+ mServiceManager = IServiceManager.Stub.asInterface(
+ Binder.allowBlocking(this.getNativeServiceManager()));
}
public IBinder asBinder() {
@@ -61,7 +62,7 @@ class ServiceManagerProxy implements IServiceManager {
@UnsupportedAppUsage
public IBinder getService(String name) throws RemoteException {
// Same as checkService (old versions of servicemanager had both methods).
- return checkService(name).getBinder();
+ return checkService(name).getServiceWithMetadata().service;
}
public Service getService2(String name) throws RemoteException {
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index 4b16c1dce463..e2169925fdd3 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -14,6 +14,8 @@
package android.os;
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
import android.util.ArraySet;
import java.util.concurrent.LinkedBlockingQueue;
@@ -93,9 +95,52 @@ public class TestLooperManager {
}
/**
- * Releases the looper to continue standard looping and processing of messages,
- * no further interactions with TestLooperManager will be allowed after
- * release() has been called.
+ * Retrieves and removes the next message that should be executed by this queue.
+ * If the queue is empty or no messages are deliverable, returns null.
+ * This method never blocks.
+ *
+ * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
+ * with it have completed.
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ @Nullable
+ public Message poll() {
+ checkReleased();
+ return mQueue.pollForTest();
+ }
+
+ /**
+ * Retrieves, but does not remove, the values of {@link Message#when} of next message that
+ * should be executed by this queue.
+ * If the queue is empty or no messages are deliverable, returns null.
+ * This method never blocks.
+ *
+ * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
+ * with it have completed.
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ @SuppressWarnings("AutoBoxing") // box the primitive long, or return null to indicate no value
+ @Nullable
+ public Long peekWhen() {
+ checkReleased();
+ return mQueue.peekWhenForTest();
+ }
+
+ /**
+ * Checks whether the Looper is currently blocked on a sync barrier.
+ *
+ * A Looper is blocked on a sync barrier if there is a Message in the Looper's
+ * queue that is ready for execution but is behind a sync barrier
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ public boolean isBlockedOnSyncBarrier() {
+ checkReleased();
+ return mQueue.isBlockedOnSyncBarrier();
+ }
+
+ /**
+ * Releases the looper to continue standard looping and processing of messages, no further
+ * interactions with TestLooperManager will be allowed after release() has been called.
*/
public void release() {
synchronized (sHeldLoopers) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7e73a5d04866..132805da7c94 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4197,12 +4197,21 @@ public class UserManager {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
private boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
- @UserIdInt int userId) {
- try {
- return mService.hasUserRestriction(restrictionKey, userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ @NonNull @UserIdInt int userId) {
+ return getUserRestrictionFromQuery(new Pair(restrictionKey, userId));
+ }
+
+ /** @hide */
+ @CachedProperty()
+ private boolean getUserRestrictionFromQuery(@NonNull Pair<String, Integer> restrictionPerUser) {
+ return UserManagerCache.getUserRestrictionFromQuery(
+ (Pair<String, Integer> q) -> mService.hasUserRestriction(q.first, q.second),
+ restrictionPerUser);
+ }
+
+ /** @hide */
+ public static final void invalidateUserRestriction() {
+ UserManagerCache.invalidateUserRestrictionFromQuery();
}
/**
@@ -6477,6 +6486,7 @@ public class UserManager {
UserManagerCache.invalidateProfileParent();
}
invalidateEnabledProfileIds();
+ invalidateUserRestriction();
}
/**
@@ -6501,7 +6511,11 @@ public class UserManager {
* @hide
*/
public static final void invalidateCacheOnUserDataChanged() {
- if (android.multiuser.Flags.cacheProfilesReadOnly()) {
+ if (android.multiuser.Flags.cacheProfilesReadOnly()
+ || android.multiuser.Flags.cacheUserInfoReadOnly()) {
+ // TODO(b/383175685): Rename the invalidation call to make it clearer that it
+ // invalidates the caches for both getProfiles and getUserInfo (since they both use the
+ // same user_manager_user_data CachedProperty.api).
UserManagerCache.invalidateProfiles();
}
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 6357baa19226..2a467386569d 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -233,6 +233,14 @@ flag {
}
flag {
+ name: "material_shape_tokens"
+ namespace: "systemui"
+ description: "Adding new Material Tokens for M3 Shape (corner radius) Spec"
+ bug: "324928718"
+ is_exported: true
+}
+
+flag {
name: "message_queue_tail_tracking"
namespace: "system_performance"
description: "track tail of message queue."
diff --git a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
index c45c51d15cc9..af56bfe50381 100644
--- a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
+++ b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
@@ -16,7 +16,7 @@
package android.os.instrumentation;
-import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.IOffsetCallback;
import android.os.instrumentation.MethodDescriptor;
import android.os.instrumentation.TargetProcess;
@@ -28,6 +28,7 @@ import android.os.instrumentation.TargetProcess;
interface IDynamicInstrumentationManager {
/** Provides ART metadata about the described compiled method within the target process */
@PermissionManuallyEnforced
- @nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
- in TargetProcess targetProcess, in MethodDescriptor methodDescriptor);
+ void getExecutableMethodFileOffsets(
+ in TargetProcess targetProcess, in MethodDescriptor methodDescriptor,
+ in IOffsetCallback callback);
}
diff --git a/core/java/android/os/instrumentation/IOffsetCallback.aidl b/core/java/android/os/instrumentation/IOffsetCallback.aidl
new file mode 100644
index 000000000000..a28c93f5353a
--- /dev/null
+++ b/core/java/android/os/instrumentation/IOffsetCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.instrumentation;
+
+import android.os.instrumentation.ExecutableMethodFileOffsets;
+
+/**
+ * System private API for providing dynamic instrumentation offset results.
+ *
+ * {@hide}
+ */
+oneway interface IOffsetCallback {
+ void onResult(in @nullable ExecutableMethodFileOffsets offsets);
+}
diff --git a/core/java/android/os/instrumentation/MethodDescriptorParser.java b/core/java/android/os/instrumentation/MethodDescriptorParser.java
new file mode 100644
index 000000000000..57fc44ff623e
--- /dev/null
+++ b/core/java/android/os/instrumentation/MethodDescriptorParser.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.os.instrumentation;
+
+import android.annotation.NonNull;
+
+import java.lang.reflect.Method;
+
+/**
+ * A utility class for dynamic instrumentation / uprobestats.
+ *
+ * @hide
+ */
+public final class MethodDescriptorParser {
+
+ /**
+ * Parses a {@link MethodDescriptor} (in string representation) into a {@link Method}.
+ */
+ public static Method parseMethodDescriptor(ClassLoader classLoader,
+ @NonNull MethodDescriptor descriptor) {
+ try {
+ Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
+ Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length];
+ for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) {
+ String typeName = descriptor.fullyQualifiedParameters[i];
+ boolean isArrayType = typeName.endsWith("[]");
+ if (isArrayType) {
+ typeName = typeName.substring(0, typeName.length() - 2);
+ }
+ switch (typeName) {
+ case "boolean":
+ parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class;
+ break;
+ case "byte":
+ parameters[i] = isArrayType ? byte.class.arrayType() : byte.class;
+ break;
+ case "char":
+ parameters[i] = isArrayType ? char.class.arrayType() : char.class;
+ break;
+ case "short":
+ parameters[i] = isArrayType ? short.class.arrayType() : short.class;
+ break;
+ case "int":
+ parameters[i] = isArrayType ? int.class.arrayType() : int.class;
+ break;
+ case "long":
+ parameters[i] = isArrayType ? long.class.arrayType() : long.class;
+ break;
+ case "float":
+ parameters[i] = isArrayType ? float.class.arrayType() : float.class;
+ break;
+ case "double":
+ parameters[i] = isArrayType ? double.class.arrayType() : double.class;
+ break;
+ default:
+ parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType()
+ : classLoader.loadClass(typeName);
+ }
+ }
+
+ return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ "The specified method cannot be found. Is this descriptor valid? "
+ + descriptor, e);
+ }
+ }
+}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 55011e52724c..b37581260bb1 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -108,6 +108,8 @@ interface IPermissionManager {
int checkUidPermission(int uid, String permissionName, int deviceId);
Map<String, PermissionState> getAllPermissionStates(String packageName, String persistentDeviceId, int userId);
+
+ int getPermissionRequestState(String packageName, String permissionName, int deviceId);
}
/**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e98397d104d6..bdf8d23438df 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1742,6 +1742,16 @@ public final class PermissionManager {
}
}
+ private static int getPermissionRequestStateUncached(String packageName, String permission,
+ int deviceId) {
+ try {
+ return AppGlobals.getPermissionManager().getPermissionRequestState(
+ packageName, permission, deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Identifies a permission query.
*
@@ -1795,14 +1805,85 @@ public final class PermissionManager {
}
}
- /** @hide */
- public static final String CACHE_KEY_PACKAGE_INFO =
+ private static final class PermissionRequestStateQuery {
+ final String mPackageName;
+ final String mPermission;
+ final int mDeviceId;
+
+ PermissionRequestStateQuery(@NonNull String packageName, @NonNull String permission,
+ int deviceId) {
+ mPackageName = packageName;
+ mPermission = permission;
+ mDeviceId = deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("PermissionRequestStateQuery(package=\"%s\","
+ + " permission=\"%s\", " + "deviceId=%d)",
+ mPackageName, mPermission, mDeviceId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mPermission, mDeviceId);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object rval) {
+ if (rval == null) {
+ return false;
+ }
+ PermissionRequestStateQuery other;
+ try {
+ other = (PermissionRequestStateQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return mDeviceId == other.mDeviceId && Objects.equals(mPackageName, other.mPackageName)
+ && Objects.equals(mPermission, other.mPermission);
+ }
+ }
+
+ // The legacy system property "package_info" had two purposes: to invalidate PIC caches and to
+ // signal that package information, and therefore permissions, might have changed.
+ // AudioSystem is the only client of the signaling behavior. The "separate permissions
+ // notification" feature splits the two behaviors into two system property names.
+ //
+ // If the feature is disabled (legacy behavior) then the two system property names have the
+ // same value. This means there is only one system property in use.
+ //
+ // If the feature is enabled, then the two system property names have different values, which
+ // means there is a system property used by PIC and a system property used for signaling. The
+ // legacy value is hard-coded in native code that relies on the signaling behavior, so the
+ // system property name for signaling is the legacy property name, and the system property
+ // name for PIC is new.
+ private static String getPackageInfoCacheKey() {
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ return PropertyInvalidatedCache.createSystemCacheKey("package_info_cache");
+ } else {
+ return CACHE_KEY_PACKAGE_INFO_NOTIFY;
+ }
+ }
+
+ /**
+ * The system property that is used to notify clients that package information, and therefore
+ * permissions, may have changed.
+ * @hide
+ */
+ public static final String CACHE_KEY_PACKAGE_INFO_NOTIFY =
PropertyInvalidatedCache.createSystemCacheKey("package_info");
+ /**
+ * The system property that is used to invalidate PIC caches.
+ * @hide
+ */
+ public static final String CACHE_KEY_PACKAGE_INFO_CACHE = getPackageInfoCacheKey();
+
/** @hide */
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
new PropertyInvalidatedCache<PermissionQuery, Integer>(
- 2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
+ 2048, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPermission") {
@Override
public Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid,
@@ -1811,10 +1892,30 @@ public final class PermissionManager {
};
/** @hide */
+ private static final PropertyInvalidatedCache<PermissionRequestStateQuery, Integer>
+ sPermissionRequestStateCache =
+ new PropertyInvalidatedCache<>(
+ 512, CACHE_KEY_PACKAGE_INFO_CACHE, "getPermissionRequestState") {
+ @Override
+ public Integer recompute(PermissionRequestStateQuery query) {
+ return getPermissionRequestStateUncached(query.mPackageName, query.mPermission,
+ query.mDeviceId);
+ }
+ };
+
+ /** @hide */
public static int checkPermission(@Nullable String permission, int pid, int uid, int deviceId) {
return sPermissionCache.query(new PermissionQuery(permission, pid, uid, deviceId));
}
+ /** @hide */
+ @Context.PermissionRequestState
+ public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
+ int deviceId) {
+ return sPermissionRequestStateCache.query(
+ new PermissionRequestStateQuery(packageName, permission, deviceId));
+ }
+
/**
* Gets the permission states for requested package and persistent device.
* <p>
@@ -1920,7 +2021,7 @@ public final class PermissionManager {
private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
sPackageNamePermissionCache =
new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
- 16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
+ 16, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPackageNamePermission") {
@Override
public Integer recompute(PackageNamePermissionQuery query) {
return checkPackageNamePermissionUncached(
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 57e1b5863a4b..70d8891f0677 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -57,7 +57,7 @@ flag {
is_exported: true
is_fixed_read_only: true
namespace: "permissions"
- description: "enable enhanced confirmation incall apis"
+ description: "DEPRECATED, does not gate any apis"
bug: "364535720"
}
@@ -340,7 +340,7 @@ flag {
is_fixed_read_only: true
namespace: "health_fitness_aconfig"
description: "This flag protects the permission that is required to call Health Connect backup and restore apis"
- bug: "376014879" # android_fr bug
+ bug: "324019102" # android_fr bug
is_exported: true
}
@@ -467,19 +467,29 @@ flag {
}
flag {
- name: "cross_user_role_platform_api_enabled"
+ name: "rate_limit_batched_note_op_async_callbacks_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Rate limit async noteOp callbacks for batched noteOperation binder call"
+ bug: "366013082"
+}
+
+flag {
+ name: "system_vendor_intelligence_role_enabled"
is_exported: true
is_fixed_read_only: true
namespace: "permissions"
- description: "Enable cross-user roles platform API"
- bug: "367732307"
+ description: "This flag is used to enable the role system_vendor_intelligence"
+ bug: "377553620"
}
flag {
- name: "rate_limit_batched_note_op_async_callbacks_enabled"
+ name: "fine_power_monitor_permission"
is_fixed_read_only: true
is_exported: true
namespace: "permissions"
- description: "Rate limit async noteOp callbacks for batched noteOperation binder call"
- bug: "366013082"
+ description: "Add support for fine-grained PowerMonitor readings"
+ bug: "341941666"
}
+
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index e4a3c9fa7741..25e8a4ddffcd 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -213,7 +213,10 @@ public abstract class PreferenceActivity extends ListActivity implements
private int mPreferenceHeaderItemResId = 0;
private boolean mPreferenceHeaderRemoveEmptyIcon = false;
+ private boolean mIsBackCallbackRegistered = false;
private final OnBackInvokedCallback mOnBackInvokedCallback = this::onBackInvoked;
+ private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
+ this::updateBackCallbackRegistrationState;
/**
* The starting request code given out to preference framework.
@@ -706,6 +709,7 @@ public abstract class PreferenceActivity extends ListActivity implements
}
}
updateBackCallbackRegistrationState();
+ getFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}
@Override
@@ -715,17 +719,25 @@ public abstract class PreferenceActivity extends ListActivity implements
private void updateBackCallbackRegistrationState() {
if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)) return;
- if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
- && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
- getOnBackInvokedDispatcher()
- .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mOnBackInvokedCallback);
- } else {
+ if ((mCurHeader != null && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null
+ && mSinglePane) || getFragmentManager().getBackStackEntryCount() != 0) {
+ if (!mIsBackCallbackRegistered) {
+ getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ }
+ } else if (mIsBackCallbackRegistered) {
getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
}
}
private void onBackInvoked() {
- if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
+ if (WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)
+ && getFragmentManager().getBackStackEntryCount() != 0) {
+ getFragmentManager().popBackStackImmediate();
+ } else if (mCurHeader != null && mSinglePane
+ && getFragmentManager().getBackStackEntryCount() == 0
&& getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
mCurHeader = null;
@@ -1012,6 +1024,7 @@ public abstract class PreferenceActivity extends ListActivity implements
@Override
protected void onDestroy() {
+ getFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
mHandler.removeMessages(MSG_BUILD_HEADERS);
super.onDestroy();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0ae9ffa655cd..c3a49305af87 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2469,6 +2469,25 @@ public final class Settings {
= "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
/**
+ * Activity Action: Show the settings for users to select their preferred SIM subscription
+ * when a new SIM subscription has become available.
+ * <p>
+ * This Activity will only launch successfully if the newly active subscription ID is set as the
+ * value of {@link #EXTRA_SUB_ID} and the value corresponds with an active SIM subscription.
+ * <p>
+ * Input: {@link #EXTRA_SUB_ID}: the subscription ID of the newly active SIM subscription.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_ACTION_SIM_PREFERENCE_SETTINGS)
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SIM_PREFERENCE_SETTINGS =
+ "android.settings.SIM_PREFERENCE_SETTINGS";
+
+ /**
* Intent Extra: The value of {@link android.app.settings.SettingsEnums#EntryPointType} for
* settings metrics that logs the entry point about physical keyboard settings.
* <p>
@@ -8931,6 +8950,18 @@ public final class Settings {
"high_text_contrast_enabled";
/**
+ * Setting that specifies the status of the High Contrast Text
+ * rectangle refresh's one-time prompt.
+ * 0 = UNKNOWN
+ * 1 = PROMPT_SHOWN
+ * 2 = PROMPT_UNNECESSARY
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_HCT_RECT_PROMPT_STATUS =
+ "accessibility_hct_rect_prompt_status";
+
+ /**
* The color contrast, float in [-1, 1], 1 being the highest contrast.
*
* @hide
@@ -10023,6 +10054,12 @@ public final class Settings {
"minimal_post_processing_allowed";
/**
+ * Whether to mirror the built-in display on all connected displays.
+ * @hide
+ */
+ public static final String MIRROR_BUILT_IN_DISPLAY = "mirror_built_in_display";
+
+ /**
* No mode switching will happen.
*
* @see #MATCH_CONTENT_FRAME_RATE
@@ -13685,6 +13722,14 @@ public final class Settings {
"render_shadows_in_compositor";
/**
+ * Policy to be used for the display shade when connected to an external display.
+ * @hide
+ */
+ @Readable
+ public static final String DEVELOPMENT_SHADE_DISPLAY_AWARENESS =
+ "shade_display_awareness";
+
+ /**
* Path to the WindowManager display settings file. If unset, the default file path will
* be used.
*
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index fff53637485b..a80be531239b 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -24,14 +24,6 @@ flag {
bug: "290696572"
}
-flag {
- name: "backup_tasks_settings_screen"
- is_exported: true
- namespace: "backstage_power"
- description: "Add a new settings page for the RUN_BACKUP_JOBS permission."
- bug: "320563660"
-}
-
# OWNER = tgunn TARGET=25Q1
flag {
name: "allow_config_maximum_call_log_entries_per_sim"
diff --git a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
index 64a3f0f60f96..568a6d7cb923 100644
--- a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
+++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
@@ -38,23 +38,30 @@ public final class DisableSecureLockDeviceParams implements Parcelable {
/**
* Client message associated with the request to disable secure lock on the device. This message
* will be shown on the device when secure lock mode is disabled.
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties such as color,
+ * font weight, or other formatting may not be honored.
*/
- private final @NonNull String mMessage;
+ private final @NonNull CharSequence mMessage;
/**
* Creates DisableSecureLockDeviceParams with the given params.
*
* @param message Allows clients to pass in a message with information about the request to
* disable secure lock on the device. This message will be shown to the user when
- * secure lock mode is disabled. If an empty string is provided, it will default
- * to a system-defined string (e.g. "Secure lock mode has been disabled.")
+ * secure lock mode is disabled. If an empty CharSequence is provided, it will
+ * default to a system-defined CharSequence (e.g. "Secure lock mode has been
+ * disabled.")
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties
+ * such as color, font weight, or other formatting may not be honored.
*/
- public DisableSecureLockDeviceParams(@NonNull String message) {
+ public DisableSecureLockDeviceParams(@NonNull CharSequence message) {
mMessage = message;
}
private DisableSecureLockDeviceParams(@NonNull Parcel in) {
- mMessage = Objects.requireNonNull(in.readString8());
+ mMessage = Objects.requireNonNull(in.readCharSequence());
}
public static final @NonNull Creator<DisableSecureLockDeviceParams> CREATOR =
@@ -77,6 +84,6 @@ public final class DisableSecureLockDeviceParams implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mMessage);
+ dest.writeCharSequence(mMessage);
}
}
diff --git a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
index 1d727727ce37..dfa391fcc85d 100644
--- a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
+++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
@@ -38,23 +38,30 @@ public final class EnableSecureLockDeviceParams implements Parcelable {
/**
* Client message associated with the request to enable secure lock on the device. This message
* will be shown on the device when secure lock mode is enabled.
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties such as color,
+ * font weight, or other formatting may not be honored.
*/
- private final @NonNull String mMessage;
+ private final @NonNull CharSequence mMessage;
/**
* Creates EnableSecureLockDeviceParams with the given params.
*
* @param message Allows clients to pass in a message with information about the request to
* enable secure lock on the device. This message will be shown to the user when
- * secure lock mode is enabled. If an empty string is provided, it will default
- * to a system-defined string (e.g. "Device is securely locked remotely.")
+ * secure lock mode is enabled. If an empty CharSequence is provided, it will
+ * default to a system-defined CharSequence (e.g. "Device is securely locked
+ * remotely.")
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties
+ * such as color, font weight, or other formatting may not be honored.
*/
- public EnableSecureLockDeviceParams(@NonNull String message) {
+ public EnableSecureLockDeviceParams(@NonNull CharSequence message) {
mMessage = message;
}
private EnableSecureLockDeviceParams(@NonNull Parcel in) {
- mMessage = Objects.requireNonNull(in.readString8());
+ mMessage = Objects.requireNonNull(in.readCharSequence());
}
public static final @NonNull Creator<EnableSecureLockDeviceParams> CREATOR =
@@ -77,6 +84,6 @@ public final class EnableSecureLockDeviceParams implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mMessage);
+ dest.writeCharSequence(mMessage);
}
}
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 34bae46b484c..ebb6fb451699 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -137,4 +137,12 @@ flag {
description: "Feature flag for Secure Lockdown feature"
bug: "373422357"
is_exported: true
+}
+
+flag {
+ name: "subscribe_to_keyguard_locked_state_perm_priv_flag"
+ namespace: "psap_ai"
+ description: "Feature flag to add the privileged flag to the SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE permission"
+ bug: "380120712"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 45cd0f011299..a46049fb2f6d 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -17,6 +17,7 @@
package android.security.net.config;
import android.util.ArraySet;
+
import java.security.cert.X509Certificate;
import java.util.Set;
@@ -24,16 +25,23 @@ import java.util.Set;
public final class CertificatesEntryRef {
private final CertificateSource mSource;
private final boolean mOverridesPins;
+ private final boolean mDisableCT;
- public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {
+ public CertificatesEntryRef(CertificateSource source, boolean overridesPins,
+ boolean disableCT) {
mSource = source;
mOverridesPins = overridesPins;
+ mDisableCT = disableCT;
}
boolean overridesPins() {
return mOverridesPins;
}
+ boolean disableCT() {
+ return mDisableCT;
+ }
+
public Set<TrustAnchor> getTrustAnchors() {
// TODO: cache this [but handle mutable sources]
Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
diff --git a/core/java/android/security/net/config/KeyStoreConfigSource.java b/core/java/android/security/net/config/KeyStoreConfigSource.java
index 8d4f098bcb37..a54d8d0499cb 100644
--- a/core/java/android/security/net/config/KeyStoreConfigSource.java
+++ b/core/java/android/security/net/config/KeyStoreConfigSource.java
@@ -17,8 +17,8 @@
package android.security.net.config;
import android.util.Pair;
+
import java.security.KeyStore;
-import java.security.KeyStoreException;
import java.util.Set;
/**
@@ -32,7 +32,7 @@ class KeyStoreConfigSource implements ConfigSource {
mConfig = new NetworkSecurityConfig.Builder()
.addCertificatesEntryRef(
// Use the KeyStore and do not override pins (of which there are none).
- new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
+ new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false, false))
.build();
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 129ae63ec9c0..410c68b8d04d 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -112,7 +112,6 @@ public final class NetworkSecurityConfig {
return mHstsEnforced;
}
- // TODO(b/28746284): add exceptions for user-added certificates and enterprise overrides.
public boolean isCertificateTransparencyVerificationRequired() {
return mCertificateTransparencyVerificationRequired;
}
@@ -192,20 +191,21 @@ public final class NetworkSecurityConfig {
* @hide
*/
public static Builder getDefaultBuilder(ApplicationInfo info) {
+ // System certificate store, does not bypass static pins, does not disable CT.
+ CertificatesEntryRef systemRef = new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false);
Builder builder = new Builder()
.setHstsEnforced(DEFAULT_HSTS_ENFORCED)
- // System certificate store, does not bypass static pins.
- .addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
+ .addCertificatesEntryRef(systemRef);
final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
&& !info.isInstantApp();
builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
// Applications targeting N and above must opt in into trusting the user added certificate
// store.
if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) {
- // User certificate store, does not bypass static pins.
+ // User certificate store, does not bypass static pins. CT is disabled.
builder.addCertificatesEntryRef(
- new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
+ new CertificatesEntryRef(UserCertificateSource.getInstance(), false, true));
}
return builder;
}
@@ -339,6 +339,16 @@ public final class NetworkSecurityConfig {
if (mCertificateTransparencyVerificationRequiredSet) {
return mCertificateTransparencyVerificationRequired;
}
+ // CT verification has not been set explicitly. Before deferring to
+ // the parent, check if any of the CertificatesEntryRef requires it
+ // to be disabled (i.e., user store or inline certificate).
+ if (hasCertificatesEntryRefs()) {
+ for (CertificatesEntryRef ref : getCertificatesEntryRefs()) {
+ if (ref.disableCT()) {
+ return false;
+ }
+ }
+ }
if (mParentBuilder != null) {
return mParentBuilder.getCertificateTransparencyVerificationRequired();
}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index b1c14793bbbd..95e579fc538b 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -182,6 +182,7 @@ public class XmlConfigSource implements ConfigSource {
boolean overridePins =
parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
int sourceId = parser.getAttributeResourceValue(null, "src", -1);
+ boolean disableCT = false;
String sourceString = parser.getAttributeValue(null, "src");
CertificateSource source = null;
if (sourceString == null) {
@@ -190,10 +191,12 @@ public class XmlConfigSource implements ConfigSource {
if (sourceId != -1) {
// TODO: Cache ResourceCertificateSources by sourceId
source = new ResourceCertificateSource(sourceId, mContext);
+ disableCT = true;
} else if ("system".equals(sourceString)) {
source = SystemCertificateSource.getInstance();
} else if ("user".equals(sourceString)) {
source = UserCertificateSource.getInstance();
+ disableCT = true;
} else if ("wfa".equals(sourceString)) {
source = WfaCertificateSource.getInstance();
} else {
@@ -201,7 +204,7 @@ public class XmlConfigSource implements ConfigSource {
+ "Should be one of system|user|@resourceVal");
}
XmlUtils.skipCurrentTag(parser);
- return new CertificatesEntryRef(source, overridePins);
+ return new CertificatesEntryRef(source, overridePins, disableCT);
}
private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 42dbd3786001..8add9f7c63cc 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -97,14 +97,6 @@ flag {
}
flag {
- name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected"
- namespace: "responsible_apis"
- description: "Prevent intent redirect attacks by showing a toast if not yet collected"
- bug: "361143368"
- is_fixed_read_only: true
-}
-
-flag {
name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected_r_w"
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by showing a toast if not yet collected"
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index fba8e42cc673..6d62a2c7db2e 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -341,7 +341,7 @@ public final class FillEventHistory implements Parcelable {
/** Credential Manager suggestions are shown instead of Autofill suggestion */
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
- public static final int UI_TYPE_CREDMAN = 4;
+ public static final int UI_TYPE_CREDENTIAL_MANAGER = 4;
/** @hide */
@IntDef(prefix = { "UI_TYPE_" }, value = {
diff --git a/services/core/java/com/android/server/notification/RateEstimator.java b/core/java/android/service/notification/RateEstimator.java
index eda96ac84b16..04789c18ffe4 100644
--- a/services/core/java/com/android/server/notification/RateEstimator.java
+++ b/core/java/android/service/notification/RateEstimator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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.
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.server.notification;
+package android.service.notification;
/**
@@ -22,7 +22,7 @@ package com.android.server.notification;
*
* {@hide}
*/
-class RateEstimator {
+public class RateEstimator {
private static final double RATE_ALPHA = 0.7;
private static final double MINIMUM_DT = 0.0005;
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
index 22b1be08e1db..06bd2555c2f8 100644
--- a/core/java/android/service/notification/ZenDeviceEffects.java
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -42,21 +42,26 @@ public final class ZenDeviceEffects implements Parcelable {
/**
* Enum for the user-modifiable fields in this object.
+ *
* @hide
*/
- @IntDef(flag = true, prefix = { "FIELD_" }, value = {
- FIELD_GRAYSCALE,
- FIELD_SUPPRESS_AMBIENT_DISPLAY,
- FIELD_DIM_WALLPAPER,
- FIELD_NIGHT_MODE,
- FIELD_DISABLE_AUTO_BRIGHTNESS,
- FIELD_DISABLE_TAP_TO_WAKE,
- FIELD_DISABLE_TILT_TO_WAKE,
- FIELD_DISABLE_TOUCH,
- FIELD_MINIMIZE_RADIO_USAGE,
- FIELD_MAXIMIZE_DOZE,
- FIELD_EXTRA_EFFECTS
- })
+ @IntDef(
+ flag = true,
+ prefix = {"FIELD_"},
+ value = {
+ FIELD_GRAYSCALE,
+ FIELD_SUPPRESS_AMBIENT_DISPLAY,
+ FIELD_DIM_WALLPAPER,
+ FIELD_NIGHT_MODE,
+ FIELD_DISABLE_AUTO_BRIGHTNESS,
+ FIELD_DISABLE_TAP_TO_WAKE,
+ FIELD_DISABLE_TILT_TO_WAKE,
+ FIELD_DISABLE_TOUCH,
+ FIELD_MINIMIZE_RADIO_USAGE,
+ FIELD_MAXIMIZE_DOZE,
+ FIELD_NIGHT_LIGHT,
+ FIELD_EXTRA_EFFECTS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ModifiableField {}
@@ -105,6 +110,9 @@ public final class ZenDeviceEffects implements Parcelable {
*/
public static final int FIELD_EXTRA_EFFECTS = 1 << 10;
+ /** @hide */
+ public static final int FIELD_NIGHT_LIGHT = 1 << 11;
+
private static final int MAX_EFFECTS_LENGTH = 2_000; // characters
private final boolean mGrayscale;
@@ -118,12 +126,22 @@ public final class ZenDeviceEffects implements Parcelable {
private final boolean mDisableTouch;
private final boolean mMinimizeRadioUsage;
private final boolean mMaximizeDoze;
+ private final boolean mNightLight;
private final Set<String> mExtraEffects;
- private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
- boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
- boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
- boolean minimizeRadioUsage, boolean maximizeDoze, Set<String> extraEffects) {
+ private ZenDeviceEffects(
+ boolean grayscale,
+ boolean suppressAmbientDisplay,
+ boolean dimWallpaper,
+ boolean nightMode,
+ boolean disableAutoBrightness,
+ boolean disableTapToWake,
+ boolean disableTiltToWake,
+ boolean disableTouch,
+ boolean minimizeRadioUsage,
+ boolean maximizeDoze,
+ boolean nightLight,
+ Set<String> extraEffects) {
mGrayscale = grayscale;
mSuppressAmbientDisplay = suppressAmbientDisplay;
mDimWallpaper = dimWallpaper;
@@ -134,6 +152,7 @@ public final class ZenDeviceEffects implements Parcelable {
mDisableTouch = disableTouch;
mMinimizeRadioUsage = minimizeRadioUsage;
mMaximizeDoze = maximizeDoze;
+ mNightLight = nightLight;
mExtraEffects = Collections.unmodifiableSet(extraEffects);
}
@@ -166,14 +185,25 @@ public final class ZenDeviceEffects implements Parcelable {
&& this.mDisableTouch == that.mDisableTouch
&& this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
&& this.mMaximizeDoze == that.mMaximizeDoze
+ && this.mNightLight == that.mNightLight
&& Objects.equals(this.mExtraEffects, that.mExtraEffects);
}
@Override
public int hashCode() {
- return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
- mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
- mMinimizeRadioUsage, mMaximizeDoze, mExtraEffects);
+ return Objects.hash(
+ mGrayscale,
+ mSuppressAmbientDisplay,
+ mDimWallpaper,
+ mNightMode,
+ mDisableAutoBrightness,
+ mDisableTapToWake,
+ mDisableTiltToWake,
+ mDisableTouch,
+ mMinimizeRadioUsage,
+ mMaximizeDoze,
+ mNightLight,
+ mExtraEffects);
}
@Override
@@ -189,6 +219,7 @@ public final class ZenDeviceEffects implements Parcelable {
if (mDisableTouch) effects.add("disableTouch");
if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
if (mMaximizeDoze) effects.add("maximizeDoze");
+ if (mNightLight) effects.add("nightLight");
if (mExtraEffects.size() > 0) {
effects.add("extraEffects=[" + String.join(",", mExtraEffects) + "]");
}
@@ -228,6 +259,9 @@ public final class ZenDeviceEffects implements Parcelable {
if ((bitmask & FIELD_MAXIMIZE_DOZE) != 0) {
modified.add("FIELD_MAXIMIZE_DOZE");
}
+ if (((bitmask) & FIELD_NIGHT_LIGHT) != 0) {
+ modified.add("FIELD_NIGHT_LIGHT");
+ }
if ((bitmask & FIELD_EXTRA_EFFECTS) != 0) {
modified.add("FIELD_EXTRA_EFFECTS");
}
@@ -313,6 +347,15 @@ public final class ZenDeviceEffects implements Parcelable {
}
/**
+ * Whether the night display transformation should be activated while the rule is active.
+ *
+ * @hide
+ */
+ public boolean shouldUseNightLight() {
+ return mNightLight;
+ }
+
+ /**
* (Immutable) set of extra effects to be applied while the rule is active. Extra effects are
* not used in AOSP, but OEMs may add support for them by providing a custom
* {@link DeviceEffectsApplier}.
@@ -329,29 +372,46 @@ public final class ZenDeviceEffects implements Parcelable {
* @hide
*/
public boolean hasEffects() {
- return mGrayscale || mSuppressAmbientDisplay || mDimWallpaper || mNightMode
- || mDisableAutoBrightness || mDisableTapToWake || mDisableTiltToWake
- || mDisableTouch || mMinimizeRadioUsage || mMaximizeDoze
+ return mGrayscale
+ || mSuppressAmbientDisplay
+ || mDimWallpaper
+ || mNightMode
+ || mDisableAutoBrightness
+ || mDisableTapToWake
+ || mDisableTiltToWake
+ || mDisableTouch
+ || mMinimizeRadioUsage
+ || mMaximizeDoze
+ || mNightLight
|| mExtraEffects.size() > 0;
}
/** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */
@NonNull
- public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() {
- @Override
- public ZenDeviceEffects createFromParcel(Parcel in) {
- return new ZenDeviceEffects(in.readBoolean(),
- in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
- in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
- in.readBoolean(),
- Set.of(in.readArray(String.class.getClassLoader(), String.class)));
- }
-
- @Override
- public ZenDeviceEffects[] newArray(int size) {
- return new ZenDeviceEffects[size];
- }
- };
+ public static final Creator<ZenDeviceEffects> CREATOR =
+ new Creator<ZenDeviceEffects>() {
+ @Override
+ public ZenDeviceEffects createFromParcel(Parcel in) {
+ return new ZenDeviceEffects(
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ in.readBoolean(),
+ Set.of(in.readArray(String.class.getClassLoader(), String.class)));
+ }
+
+ @Override
+ public ZenDeviceEffects[] newArray(int size) {
+ return new ZenDeviceEffects[size];
+ }
+ };
@Override
public int describeContents() {
@@ -370,6 +430,7 @@ public final class ZenDeviceEffects implements Parcelable {
dest.writeBoolean(mDisableTouch);
dest.writeBoolean(mMinimizeRadioUsage);
dest.writeBoolean(mMaximizeDoze);
+ dest.writeBoolean(mNightLight);
dest.writeArray(mExtraEffects.toArray(new String[0]));
}
@@ -387,6 +448,7 @@ public final class ZenDeviceEffects implements Parcelable {
private boolean mDisableTouch;
private boolean mMinimizeRadioUsage;
private boolean mMaximizeDoze;
+ private boolean mNightLight;
private final HashSet<String> mExtraEffects = new HashSet<>();
/**
@@ -410,6 +472,7 @@ public final class ZenDeviceEffects implements Parcelable {
mDisableTouch = zenDeviceEffects.shouldDisableTouch();
mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
+ mNightLight = zenDeviceEffects.shouldUseNightLight();
mExtraEffects.addAll(zenDeviceEffects.getExtraEffects());
}
@@ -512,6 +575,18 @@ public final class ZenDeviceEffects implements Parcelable {
}
/**
+ * Sets whether the night display transformation should be activated while the rule is
+ * active.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldUseNightLight(boolean nightLight) {
+ mNightLight = nightLight;
+ return this;
+ }
+
+ /**
* Sets the extra effects to be applied while the rule is active. Extra effects are not
* used in AOSP, but OEMs may add support for them by providing a custom
* {@link DeviceEffectsApplier}.
@@ -577,6 +652,7 @@ public final class ZenDeviceEffects implements Parcelable {
if (effects.shouldDisableTouch()) setShouldDisableTouch(true);
if (effects.shouldMinimizeRadioUsage()) setShouldMinimizeRadioUsage(true);
if (effects.shouldMaximizeDoze()) setShouldMaximizeDoze(true);
+ if (effects.shouldUseNightLight()) setShouldUseNightLight(true);
addExtraEffects(effects.getExtraEffects());
return this;
}
@@ -584,10 +660,19 @@ public final class ZenDeviceEffects implements Parcelable {
/** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
@NonNull
public ZenDeviceEffects build() {
- return new ZenDeviceEffects(mGrayscale,
- mSuppressAmbientDisplay, mDimWallpaper, mNightMode, mDisableAutoBrightness,
- mDisableTapToWake, mDisableTiltToWake, mDisableTouch, mMinimizeRadioUsage,
- mMaximizeDoze, mExtraEffects);
+ return new ZenDeviceEffects(
+ mGrayscale,
+ mSuppressAmbientDisplay,
+ mDimWallpaper,
+ mNightMode,
+ mDisableAutoBrightness,
+ mDisableTapToWake,
+ mDisableTiltToWake,
+ mDisableTouch,
+ mMinimizeRadioUsage,
+ mMaximizeDoze,
+ mNightLight,
+ mExtraEffects);
}
}
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 13887781f1ec..4f459aa9131a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -325,6 +325,7 @@ public class ZenModeConfig implements Parcelable {
private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch";
private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage";
private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze";
+ private static final String DEVICE_EFFECT_USE_NIGHT_LIGHT = "zdeUseNightLight";
private static final String DEVICE_EFFECT_EXTRAS = "zdeExtraEffects";
private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields";
@@ -1508,25 +1509,32 @@ public class ZenModeConfig implements Parcelable {
@FlaggedApi(Flags.FLAG_MODES_API)
@Nullable
private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) {
- ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
- .setShouldDisplayGrayscale(
- safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
- .setShouldSuppressAmbientDisplay(
- safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
- .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
- .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
- .setShouldDisableAutoBrightness(
- safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
- .setShouldDisableTapToWake(
- safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
- .setShouldDisableTiltToWake(
- safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
- .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
- .setShouldMinimizeRadioUsage(
- safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
- .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
- .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS))
- .build();
+ ZenDeviceEffects deviceEffects =
+ new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(
+ safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
+ .setShouldSuppressAmbientDisplay(
+ safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
+ .setShouldDimWallpaper(
+ safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
+ .setShouldUseNightMode(
+ safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
+ .setShouldDisableAutoBrightness(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
+ .setShouldDisableTapToWake(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
+ .setShouldDisableTiltToWake(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
+ .setShouldDisableTouch(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
+ .setShouldMinimizeRadioUsage(
+ safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
+ .setShouldMaximizeDoze(
+ safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
+ .setShouldUseNightLight(
+ safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_LIGHT, false))
+ .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS))
+ .build();
return deviceEffects.hasEffects() ? deviceEffects : null;
}
@@ -1550,6 +1558,7 @@ public class ZenModeConfig implements Parcelable {
writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
deviceEffects.shouldMinimizeRadioUsage());
writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_LIGHT, deviceEffects.shouldUseNightLight());
writeStringSet(out, DEVICE_EFFECT_EXTRAS, deviceEffects.getExtraEffects());
}
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index c9f464716e72..31acd248dcc0 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -714,6 +714,7 @@ public class ZenModeDiff {
public static final String FIELD_DISABLE_TOUCH = "mDisableTouch";
public static final String FIELD_MINIMIZE_RADIO_USAGE = "mMinimizeRadioUsage";
public static final String FIELD_MAXIMIZE_DOZE = "mMaximizeDoze";
+ public static final String FIELD_NIGHT_LIGHT = "mNightLight";
public static final String FIELD_EXTRA_EFFECTS = "mExtraEffects";
// NOTE: new field strings must match the variable names in ZenDeviceEffects
@@ -781,6 +782,11 @@ public class ZenModeDiff {
addField(FIELD_MAXIMIZE_DOZE, new FieldDiff<>(from.shouldMaximizeDoze(),
to.shouldMaximizeDoze()));
}
+ if (from.shouldUseNightLight() != to.shouldUseNightLight()) {
+ addField(
+ FIELD_NIGHT_LIGHT,
+ new FieldDiff<>(from.shouldUseNightLight(), to.shouldUseNightLight()));
+ }
if (!Objects.equals(from.getExtraEffects(), to.getExtraEffects())) {
addField(FIELD_EXTRA_EFFECTS, new FieldDiff<>(from.getExtraEffects(),
to.getExtraEffects()));
diff --git a/core/java/android/service/ondeviceintelligence/OWNERS b/core/java/android/service/ondeviceintelligence/OWNERS
deleted file mode 100644
index 09774f78d712..000000000000
--- a/core/java/android/service/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
index 1acb7b8460cf..ea7d4a675713 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
@@ -187,27 +187,39 @@ public final class SettingsPreferenceMetadata implements Parcelable {
/** @hide */
@IntDef(value = {
- NOT_SENSITIVE,
- SENSITIVE,
- INTENT_ONLY
+ NO_SENSITIVITY,
+ EXPECT_POST_CONFIRMATION,
+ EXPECT_PRE_CONFIRMATION,
+ NO_DIRECT_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteSensitivity {}
/**
- * Preference is not sensitive, thus its value is writable without explicit consent, assuming
- * all necessary permissions are granted.
+ * Indicates preference is not sensitive.
+ * <p>Its value is writable without explicit consent, assuming all necessary permissions are
+ * granted.
*/
- public static final int NOT_SENSITIVE = 0;
+ public static final int NO_SENSITIVITY = 0;
/**
- * Preference is sensitive, meaning that in addition to necessary permissions, writing its value
- * will also request explicit user consent.
+ * Indicates preference is mildly sensitive.
+ * <p>In addition to necessary permissions, after writing its value the user should be
+ * given the option to revert back.
*/
- public static final int SENSITIVE = 1;
+ public static final int EXPECT_POST_CONFIRMATION = 1;
/**
- * Preference is not permitted for write-access via API and must be changed via Settings page.
+ * Indicates preference is sensitive.
+ * <p>In addition to necessary permissions, the user should be prompted for confirmation prior
+ * to making a change. Otherwise it is suggested to provide a deeplink to the Preference's page
+ * instead, accessible via {@link #getLaunchIntent}.
*/
- public static final int INTENT_ONLY = 2;
+ public static final int EXPECT_PRE_CONFIRMATION = 2;
+ /**
+ * Indicates preference is highly sensitivity and carries significant user-risk.
+ * <p>This Preference cannot be changed through this API and no direct deeplink is available.
+ * Other Metadata is still available.
+ */
+ public static final int NO_DIRECT_ACCESS = 3;
private SettingsPreferenceMetadata(@NonNull Builder builder) {
mKey = builder.mKey;
@@ -303,7 +315,7 @@ public final class SettingsPreferenceMetadata implements Parcelable {
private boolean mAvailable = false;
private boolean mWritable = false;
private boolean mRestricted = false;
- @WriteSensitivity private int mSensitivity = INTENT_ONLY;
+ @WriteSensitivity private int mSensitivity = NO_DIRECT_ACCESS;
private Intent mLaunchIntent;
private Bundle mExtras;
@@ -436,6 +448,9 @@ public final class SettingsPreferenceMetadata implements Parcelable {
*/
@NonNull
public SettingsPreferenceMetadata build() {
+ if (mSensitivity == NO_DIRECT_ACCESS) {
+ mLaunchIntent = null;
+ }
return new SettingsPreferenceMetadata(this);
}
}
diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags
index e209a286966a..5ba2baec6fcf 100644
--- a/core/java/android/speech/tts/EventLogTags.logtags
+++ b/core/java/android/speech/tts/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.speech.tts;
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index cb72b976c784..6dc82c40ddc5 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -678,7 +678,7 @@ public class TextUtils {
* @return true if a and b are equal
*/
@android.ravenwood.annotation.RavenwoodKeep
- public static boolean equals(CharSequence a, CharSequence b) {
+ public static boolean equals(@Nullable CharSequence a, @Nullable CharSequence b) {
if (a == b) return true;
int length;
if (a != null && b != null && (length = a.length()) == b.length()) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 8cb96ae1d611..089b5c256b6e 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -42,6 +42,7 @@ import android.view.animation.AnimationUtils;
import java.io.PrintWriter;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Coordinates the timing of animations, input and drawing.
@@ -208,7 +209,7 @@ public final class Choreographer {
private final FrameData mFrameData = new FrameData();
private volatile boolean mInDoFrameCallback = false;
- private static class BufferStuffingData {
+ private static class BufferStuffingState {
enum RecoveryAction {
// No recovery
NONE,
@@ -218,21 +219,15 @@ public final class Choreographer {
// back toward threshold.
DELAY_FRAME
}
- // The maximum number of times frames will be delayed per buffer stuffing event.
- // Since buffer stuffing can persist for several consecutive frames following the
- // initial missed frame, we want to adjust the timeline with enough frame delays and
- // offsets to return the queued buffer count back to threshold.
- public static final int MAX_FRAME_DELAYS = 3;
+ // Indicates if recovery should begin. Is true whenever the client was blocked
+ // on dequeuing a buffer. When buffer stuffing recovery begins, this is reset
+ // since the scheduled frame delay reduces the number of queued buffers.
+ public AtomicBoolean isStuffed = new AtomicBoolean(false);
// Whether buffer stuffing recovery has begun. Recovery can only end
// when events are idle.
public boolean isRecovering = false;
- // The number of frames delayed so far during recovery. Used to compare with
- // MAX_FRAME_DELAYS to safeguard against excessive frame delays during recovery.
- // Also used as unique cookie for tracing.
- public int numberFrameDelays = 0;
-
// The number of additional frame delays scheduled during recovery to wait for the next
// vsync. These are scheduled when frame times appear to go backward or frames are
// being skipped due to FPSDivisor.
@@ -245,12 +240,20 @@ public final class Choreographer {
*/
public void reset() {
isRecovering = false;
- numberFrameDelays = 0;
numberWaitsForNextVsync = 0;
}
}
- private final BufferStuffingData mBufferStuffingData = new BufferStuffingData();
+ private final BufferStuffingState mBufferStuffingState = new BufferStuffingState();
+
+ /**
+ * Set flag to indicate that client is blocked waiting for buffer release and
+ * buffer stuffing recovery should soon begin.
+ * @hide
+ */
+ public void onWaitForBufferRelease() {
+ mBufferStuffingState.isStuffed.set(true);
+ }
/**
* Contains information about the current frame for jank-tracking,
@@ -901,67 +904,56 @@ public final class Choreographer {
// Conducts logic for beginning or ending buffer stuffing recovery.
// Returns an enum for the recovery action that should be taken in doFrame().
- BufferStuffingData.RecoveryAction checkBufferStuffingRecovery(long frameTimeNanos,
+ BufferStuffingState.RecoveryAction updateBufferStuffingState(long frameTimeNanos,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
- // Canned animations can recover from buffer stuffing whenever more
- // than 2 buffers are queued.
- if (vsyncEventData.numberQueuedBuffers > 2) {
- mBufferStuffingData.isRecovering = true;
- // Intentional frame delay that can happen at most MAX_FRAME_DELAYS times per
- // buffer stuffing event until the buffer count returns to threshold. The
- // delayed frames are compensated for by the negative offsets added to the
- // animation timestamps.
- if (mBufferStuffingData.numberFrameDelays < mBufferStuffingData.MAX_FRAME_DELAYS) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.asyncTraceForTrackBegin(
- Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread "
- + android.os.Process.myTid() + ", recover frame #"
- + mBufferStuffingData.numberFrameDelays,
- mBufferStuffingData.numberFrameDelays);
- }
- mBufferStuffingData.numberFrameDelays++;
- scheduleVsyncLocked();
- return BufferStuffingData.RecoveryAction.DELAY_FRAME;
+ if (!mBufferStuffingState.isRecovering) {
+ if (!mBufferStuffingState.isStuffed.getAndSet(false)) {
+ return BufferStuffingState.RecoveryAction.NONE;
+ }
+ // Canned animations can recover from buffer stuffing whenever the
+ // client is blocked on dequeueBuffer. Frame delay only occurs at
+ // the start of recovery to free a buffer.
+ mBufferStuffingState.isRecovering = true;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.asyncTraceForTrackBegin(
+ Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread "
+ + android.os.Process.myTid() + ", recover frame", 0);
}
+ return BufferStuffingState.RecoveryAction.DELAY_FRAME;
}
- if (mBufferStuffingData.isRecovering) {
- // Includes an additional expected frame delay from the natural scheduling
- // of the next vsync event.
- int totalFrameDelays = mBufferStuffingData.numberFrameDelays
- + mBufferStuffingData.numberWaitsForNextVsync + 1;
- long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
- ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
-
- // Detected idle state due to a longer inactive period since the last vsync callback
- // than the total expected number of vsync frame delays. End buffer stuffing recovery.
- // There are no frames to animate and offsets no longer need to be added
- // since the idle state gives the animation a chance to catch up.
- if (vsyncsSinceLastCallback > totalFrameDelays) {
- if (DEBUG_JANK) {
- Log.d(TAG, "End buffer stuffing recovery");
- }
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- for (int i = 0; i < mBufferStuffingData.numberFrameDelays; i++) {
- Trace.asyncTraceForTrackEnd(
- Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", i);
- }
- }
- mBufferStuffingData.reset();
-
- } else {
- if (DEBUG_JANK) {
- Log.d(TAG, "Adjust animation timeline with a negative offset");
- }
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.instantForTrack(
- Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery",
- "Negative offset added to animation");
- }
- return BufferStuffingData.RecoveryAction.OFFSET;
+ // Total number of frame delays used to detect idle state. Includes an additional
+ // expected frame delay from the natural scheduling of the next vsync event and
+ // the intentional frame delay that was scheduled when stuffing was first detected.
+ int totalFrameDelays = mBufferStuffingState.numberWaitsForNextVsync + 2;
+ long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
+ ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
+
+ // Detected idle state due to a longer inactive period since the last vsync callback
+ // than the total expected number of vsync frame delays. End buffer stuffing recovery.
+ // There are no frames to animate and offsets no longer need to be added
+ // since the idle state gives the animation a chance to catch up.
+ if (vsyncsSinceLastCallback > totalFrameDelays) {
+ if (DEBUG_JANK) {
+ Log.d(TAG, "End buffer stuffing recovery");
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.asyncTraceForTrackEnd(
+ Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", 0);
+ }
+ mBufferStuffingState.reset();
+ return BufferStuffingState.RecoveryAction.NONE;
+ }
+
+ if (DEBUG_JANK) {
+ Log.d(TAG, "Adjust animation timeline with a negative offset");
+ }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instantForTrack(
+ Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery",
+ "Negative offset added to animation");
}
- return BufferStuffingData.RecoveryAction.NONE;
+ return BufferStuffingState.RecoveryAction.OFFSET;
}
void doFrame(long frameTimeNanos, int frame,
@@ -973,7 +965,7 @@ public final class Choreographer {
// Evaluate if buffer stuffing recovery needs to start or end, and
// what actions need to be taken for recovery.
- switch (checkBufferStuffingRecovery(frameTimeNanos, vsyncEventData)) {
+ switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
case NONE:
// Without buffer stuffing recovery, offsetFrameTimeNanos is
// synonymous with frameTimeNanos.
@@ -984,7 +976,8 @@ public final class Choreographer {
offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
break;
case DELAY_FRAME:
- // Intentional frame delay to help restore queued buffer count to threshold.
+ // Intentional frame delay to help reduce queued buffer count.
+ scheduleVsyncLocked();
return;
default:
break;
@@ -1037,7 +1030,7 @@ public final class Choreographer {
+ " ms in the past.");
}
}
- if (mBufferStuffingData.isRecovering) {
+ if (mBufferStuffingState.isRecovering) {
frameTimeNanos -= frameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Adjusted animation timeline with a negative offset after"
@@ -1055,8 +1048,8 @@ public final class Choreographer {
+ "previously skipped frame. Waiting for next vsync.");
}
traceMessage("Frame time goes backward");
- if (mBufferStuffingData.isRecovering) {
- mBufferStuffingData.numberWaitsForNextVsync++;
+ if (mBufferStuffingState.isRecovering) {
+ mBufferStuffingState.numberWaitsForNextVsync++;
}
scheduleVsyncLocked();
return;
@@ -1066,8 +1059,8 @@ public final class Choreographer {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
- if (mBufferStuffingData.isRecovering) {
- mBufferStuffingData.numberWaitsForNextVsync++;
+ if (mBufferStuffingState.isRecovering) {
+ mBufferStuffingState.numberWaitsForNextVsync++;
}
scheduleVsyncLocked();
return;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c9d560c3424b..0c8a0d60a96a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@ package android.view;
import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES;
import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
@@ -58,6 +59,7 @@ import android.os.SystemClock;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.TypedValue;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1048,6 +1050,19 @@ public final class Display {
}
/**
+ * Returns the smallest size of the display in dp
+ * @hide
+ */
+ public float getMinSizeDimensionDp() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ mDisplayInfo.getAppMetrics(mTempMetrics);
+ return TypedValue.deriveDimension(COMPLEX_UNIT_DIP,
+ Math.min(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight), mTempMetrics);
+ }
+ }
+
+ /**
* @deprecated Use {@link WindowMetrics#getBounds#width()} instead.
*/
@Deprecated
@@ -1301,17 +1316,23 @@ public final class Display {
}
/**
- * Represents the {@link FrameRateCategory} for the Normal frame rate
+ * Normal category determines the framework's recommended normal frame rate.
+ * Opt for this normal rate unless a higher frame rate significantly enhances
+ * the user experience.
*
- * @see FrameRateCategory
+ * @see #getSuggestedFrameRate(int)
+ * @see #FRAME_RATE_CATEGORY_HIGH
*/
@FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
public static final int FRAME_RATE_CATEGORY_NORMAL = 0;
/**
- * Represents the {@link FrameRateCategory} for the High frame rate
+ * High category determines the framework's recommended high frame rate.
+ * Opt for this high rate when a higher frame rate significantly enhances
+ * the user experience.
*
- * @see FrameRateCategory
+ * @see #getSuggestedFrameRate(int)
+ * @see #FRAME_RATE_CATEGORY_NORMAL
*/
@FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
public static final int FRAME_RATE_CATEGORY_HIGH = 1;
@@ -1332,22 +1353,17 @@ public final class Display {
*
* <p> For example, an animation that does not require fast render rates can use
* the {@link #FRAME_RATE_CATEGORY_NORMAL} to get the suggested frame rate.
- * The suggested frame rate then can be used in the
- * {@link Surface.FrameRateParams.Builder#setDesiredRateRange} for desiredMinRate.
*
* <pre>{@code
* float desiredMinRate = display.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL);
- * Surface.FrameRateParams params = new Surface.FrameRateParams.Builder().
- * setDesiredRateRange(desiredMinRate, Float.MAX).build();
- * surface.setFrameRate(params);
+ * surface.setFrameRate(desiredMinRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
* }</pre>
* </p>
*
* @param category either {@link #FRAME_RATE_CATEGORY_NORMAL}
* or {@link #FRAME_RATE_CATEGORY_HIGH}
*
- * @see Surface#setFrameRate(Surface.FrameRateParams)
- * @see SurfaceControl.Transaction#setFrameRate(SurfaceControl, Surface.FrameRateParams)
+ * @see Surface#setFrameRate(float, int)
* @throws IllegalArgumentException when category is not {@link #FRAME_RATE_CATEGORY_NORMAL}
* or {@link #FRAME_RATE_CATEGORY_HIGH}
*/
@@ -2040,6 +2056,22 @@ public final class Display {
}
/**
+ * Returns whether the display is eligible for hosting tasks.
+ *
+ * For example, if the display is used for mirroring, this will return {@code false}.
+ *
+ * TODO (b/383666349): Rename this later once there is a better option.
+ *
+ * @hide
+ */
+ public boolean canHostTasks() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mIsValid && mDisplayInfo.canHostTasks;
+ }
+ }
+
+ /**
* Returns true if the specified UID has access to this display.
* @hide
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 43078847326c..ba098eb53246 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -408,6 +408,15 @@ public final class DisplayInfo implements Parcelable {
@Nullable
public String thermalBrightnessThrottlingDataId;
+ /**
+ * Indicates whether the display is eligible for hosting tasks.
+ *
+ * For example, if the display is used for mirroring, this will be {@code false}.
+ *
+ * @hide
+ */
+ public boolean canHostTasks;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -493,7 +502,8 @@ public final class DisplayInfo implements Parcelable {
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
&& thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling)
&& Objects.equals(
- thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId);
+ thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId)
+ && canHostTasks == other.canHostTasks;
}
@Override
@@ -561,6 +571,7 @@ public final class DisplayInfo implements Parcelable {
hdrSdrRatio = other.hdrSdrRatio;
thermalRefreshRateThrottling = other.thermalRefreshRateThrottling;
thermalBrightnessThrottlingDataId = other.thermalBrightnessThrottlingDataId;
+ canHostTasks = other.canHostTasks;
}
public void readFromParcel(Parcel source) {
@@ -642,6 +653,7 @@ public final class DisplayInfo implements Parcelable {
thermalRefreshRateThrottling = source.readSparseArray(null,
SurfaceControl.RefreshRateRange.class);
thermalBrightnessThrottlingDataId = source.readString8();
+ canHostTasks = source.readBoolean();
}
@Override
@@ -717,6 +729,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeFloat(hdrSdrRatio);
dest.writeSparseArray(thermalRefreshRateThrottling);
dest.writeString8(thermalBrightnessThrottlingDataId);
+ dest.writeBoolean(canHostTasks);
}
@Override
@@ -1020,6 +1033,8 @@ public final class DisplayInfo implements Parcelable {
sb.append(thermalRefreshRateThrottling);
sb.append(", thermalBrightnessThrottlingDataId ");
sb.append(thermalBrightnessThrottlingDataId);
+ sb.append(", canHostTasks ");
+ sb.append(canHostTasks);
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/EventLogTags.logtags b/core/java/android/view/EventLogTags.logtags
index f3792930647a..95894fa32d6b 100644
--- a/core/java/android/view/EventLogTags.logtags
+++ b/core/java/android/view/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.view
@@ -35,7 +35,7 @@ option java_package android.view
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
# 32000 - 32999 reserved for input method framework
# IME animation is started.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6d85e7589c48..072a037aa84a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -766,7 +766,7 @@ interface IWindowManager
* container.
*/
@EnforcePermission("MANAGE_APP_TOKENS")
- void updateDisplayWindowRequestedVisibleTypes(int displayId, int requestedVisibleTypes,
+ void updateDisplayWindowRequestedVisibleTypes(int displayId, int visibleTypes, int mask,
in @nullable ImeTracker.Token statsToken);
/**
@@ -933,6 +933,27 @@ interface IWindowManager
void detachWindowContext(IBinder clientToken);
/**
+ * Reparents the {@link android.window.WindowContext} to the
+ * {@link com.android.server.wm.DisplayArea} on another display.
+ * This method also reparent the WindowContext associated WindowToken to another display if
+ * necessary.
+ * <p>
+ * {@code type} and {@code options} must be the same as the previous call of
+ * {@link #attachWindowContextToDisplayArea} on the same Context otherwise this will fail
+ * silently.
+ *
+ * @param appThread the process that the window context is on.
+ * @param clientToken the window context's token
+ * @param type The window type of the WindowContext
+ * @param displayId The new display id this context windows should be parented to
+ * @param options Bundle the context was created with
+ *
+ * @return True if the operation was successful, False otherwise.
+ */
+ boolean reparentWindowContextToDisplayArea(in IApplicationThread appThread,
+ IBinder clientToken, int displayId);
+
+ /**
* Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
*
* @param listener the listener to be registered
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index b796e0b1c429..ba208390c8b7 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -357,6 +357,31 @@ public class InsetsSource implements Parcelable {
} else if (mTmpFrame.right == relativeFrame.right) {
return Insets.of(0, 0, mTmpFrame.width(), 0);
}
+ } else {
+ // The source doesn't cover the width or the height of relativeFrame, but just parts of
+ // them. Here uses mSideHint to decide which side should be inset.
+ switch (mSideHint) {
+ case SIDE_LEFT:
+ if (mTmpFrame.left == relativeFrame.left) {
+ return Insets.of(mTmpFrame.width(), 0, 0, 0);
+ }
+ break;
+ case SIDE_TOP:
+ if (mTmpFrame.top == relativeFrame.top) {
+ return Insets.of(0, mTmpFrame.height(), 0, 0);
+ }
+ break;
+ case SIDE_RIGHT:
+ if (mTmpFrame.right == relativeFrame.right) {
+ return Insets.of(0, 0, mTmpFrame.width(), 0);
+ }
+ break;
+ case SIDE_BOTTOM:
+ if (mTmpFrame.bottom == relativeFrame.bottom) {
+ return Insets.of(0, 0, 0, mTmpFrame.height());
+ }
+ break;
+ }
}
return Insets.NONE;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 67050e01b591..213ece09da22 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -296,8 +296,8 @@ public class InsetsState implements Parcelable {
@SoftInputModeFlags int softInputMode, int windowFlags) {
final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
- ? systemBars() | ime()
- : systemBars();
+ ? systemBars() | displayCutout() | ime()
+ : systemBars() | displayCutout();
@InsetsType int forceConsumingTypes = 0;
Insets insets = Insets.NONE;
for (int i = mSources.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 049189f8af8d..d13f0e21bf80 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17184,7 +17184,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
final Insets insets = metrics.getWindowInsets().getInsets(
- WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
outRect.set(metrics.getBounds());
outRect.inset(insets);
outRect.offsetTo(0, 0);
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 63bf392b5ef1..9e97a8eb58aa 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -591,7 +591,7 @@ public class ViewConfiguration {
res.getBoolean(
com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
mViewTouchScreenHapticScrollFeedbackEnabled =
- Flags.enableTouchScrollFeedback()
+ Flags.enableScrollFeedbackForTouch()
? res.getBoolean(
com.android.internal.R.bool
.config_viewTouchScreenHapticScrollFeedbackEnabled)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e9c2cf8576cd..1d27574eca8c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -17,6 +17,7 @@
package android.view;
import static android.adpf.Flags.adpfViewrootimplActionDownBoost;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
@@ -2254,7 +2255,7 @@ public final class ViewRootImpl implements ViewParent,
onClientWindowFramesChanged(frames);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mergedConfiguration);
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
final Rect attachedFrame = frames.attachedFrame;
@@ -2771,6 +2772,7 @@ public final class ViewRootImpl implements ViewParent,
mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
+ mBlastBufferQueue.setWaitForBufferReleaseCallback(mChoreographer::onWaitForBufferRelease);
// If we create and destroy BBQ without recreating the SurfaceControl, we can end up
// queuing buffers on multiple apply tokens causing out of order buffer submissions. We
// fix this by setting the same apply token on all BBQs created by this VRI.
@@ -4433,7 +4435,8 @@ public final class ViewRootImpl implements ViewParent,
// merged with a sync group or BLASTBufferQueue before making it to this point
// But better a one or two frame flicker than steady-state broken from dropping
// whatever is in this transaction
- mPendingTransaction.apply();
+ // apply immediately with bbq apply token
+ mergeWithNextTransaction(mPendingTransaction, 0);
mHasPendingTransactions = false;
}
mSyncBuffer = false;
@@ -5499,7 +5502,8 @@ public final class ViewRootImpl implements ViewParent,
Log.d(mTag, "Pending transaction will not be applied in sync with a draw due to "
+ logReason);
}
- pendingTransaction.apply();
+ // apply immediately with bbq apply token
+ mergeWithNextTransaction(pendingTransaction, 0);
}
}
/**
@@ -7977,8 +7981,9 @@ public final class ViewRootImpl implements ViewParent,
}
private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
- if (getConfiguration().windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_MULTI_WINDOW) {
+ final int windowingMode = getConfiguration().windowConfiguration.getWindowingMode();
+ if (windowingMode != WINDOWING_MODE_MULTI_WINDOW
+ && windowingMode != WINDOWING_MODE_FREEFORM) {
return false;
}
try {
@@ -9458,7 +9463,7 @@ public final class ViewRootImpl implements ViewParent,
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
}
mInvCompatScale = 1f / mTmpFrames.compatScale;
- CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mPendingMergedConfiguration);
handleInsetsControlChanged(mTempInsets, mTempControls);
mPendingAlwaysConsumeSystemBars =
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 1be7f4849f07..43a946a234ba 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -87,8 +87,12 @@ public abstract class ViewStructure {
* <p>This value is added to mainly help with debugging purpose.
*/
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @SuppressWarnings(
+ "ActionValue") // Lint expects this as
+ // android.view.contentcapture.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER
+ // but should not have contentcapture
public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE =
- "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
+ "android.view.extra.VIRTUAL_STRUCTURE_TYPE";
/**
* Key used for specifying the version of the view that generated the virtual structure for
@@ -98,8 +102,12 @@ public abstract class ViewStructure {
* "104.0.5112.69", then the value should be "104.0.5112.69"
*/
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @SuppressWarnings(
+ "ActionValue") // Lint expects this as
+ // android.view.contentcapture.extra.VIRTUAL_STRUCTURE_TYPE
+ // but should not have contentcapture
public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER =
- "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
+ "android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
/**
* Set the identifier for this view.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index df0c5a34e992..8a10979eb3c9 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -6540,6 +6540,15 @@ public class AccessibilityNodeInfo implements Parcelable {
* Class with information if a node is a range.
*/
public static final class RangeInfo {
+ /** @hide */
+ @IntDef(prefix = { "RANGE_TYPE_" }, value = {
+ RANGE_TYPE_INT,
+ RANGE_TYPE_FLOAT,
+ RANGE_TYPE_PERCENT,
+ RANGE_TYPE_INDETERMINATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RangeType {}
/** Range type: integer. */
public static final int RANGE_TYPE_INT = 0;
@@ -6588,7 +6597,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param current The current value.
*/
@Deprecated
- public static RangeInfo obtain(int type, float min, float max, float current) {
+ public static RangeInfo obtain(@RangeType int type, float min, float max, float current) {
return new RangeInfo(type, min, max, current);
}
@@ -6602,7 +6611,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* maximum.
* @param current The current value.
*/
- public RangeInfo(int type, float min, float max, float current) {
+ public RangeInfo(@RangeType int type, float min, float max, float current) {
mType = type;
mMin = min;
mMax = max;
@@ -6618,6 +6627,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @see #RANGE_TYPE_FLOAT
* @see #RANGE_TYPE_PERCENT
*/
+ @RangeType
public int getType() {
return mType;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1de0474182dd..60e528c5fb58 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.service.autofill.Flags.relayoutFix;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
@@ -787,6 +788,11 @@ public final class AutofillManager {
private AutofillStateFingerprint mAutofillStateFingerprint;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ */
+ private boolean mImproveFillDialogEnabled;
+
/** @hide */
public interface AutofillClient {
/**
@@ -1017,6 +1023,17 @@ public final class AutofillManager {
mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ }
+
+ /**
+ * Whether improvement to fill dialog is enabled.
+ *
+ * @hide
+ */
+ public boolean isImproveFillDialogEnabled() {
+ return mImproveFillDialogEnabled;
}
/**
@@ -1679,6 +1696,11 @@ public final class AutofillManager {
private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints,
boolean isCredmanRequested) {
+ if (isImproveFillDialogEnabled() && !isCredmanRequested) {
+ // We do not want to send pre-trigger request.
+ // TODO(b/377868687): verify if we can remove the flow for isCredmanRequested too.
+ return;
+ }
if (sDebug) {
Log.d(TAG, "notifyViewReadyInner:" + id);
}
@@ -2046,6 +2068,34 @@ public final class AutofillManager {
}
/**
+ * Notify autofill system that IME animation has started
+ * @param startTimeMs start time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationStart(long startTimeMs) {
+ try {
+ mService.notifyImeAnimationStart(mSessionId, startTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
+ * Notify autofill system that IME animation has ended
+ * @param endTimeMs end time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationEnd(long endTimeMs) {
+ try {
+ mService.notifyImeAnimationEnd(mSessionId, endTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
* Called when a virtual view that supports autofill is exited.
*
* @param view the virtual view parent.
@@ -4050,6 +4100,10 @@ public final class AutofillManager {
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, view.getAutofillId())) {
mShowAutofillDialogCalled = true;
@@ -4093,6 +4147,10 @@ public final class AutofillManager {
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
mShowAutofillDialogCalled = true;
@@ -4117,7 +4175,7 @@ public final class AutofillManager {
return false;
}
- if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
+ if (getImeStateFlag(view) == FLAG_IME_SHOWING && !isImproveFillDialogEnabled()) {
// IME is showing
return false;
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index f67405f7c1e4..28f8577beed7 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -70,4 +70,6 @@ oneway interface IAutoFillManager {
void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
+ void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId);
+ void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId);
}
diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig
index 9080b1669ed5..6dccbad3b6a9 100644
--- a/core/java/android/view/flags/scroll_capture.aconfig
+++ b/core/java/android/view/flags/scroll_capture.aconfig
@@ -11,3 +11,12 @@ flag {
}
}
+flag {
+ name: "scroll_capture_relax_scroll_view_criteria"
+ namespace: "systemui"
+ description: "Treat all custom ViewGroups which support scrollTo as ScrollView"
+ bug: "189827634"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index ebda4d472b0d..ddf6ff11a83a 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -17,10 +17,10 @@ flag {
}
flag {
- namespace: "toolkit"
- name: "enable_touch_scroll_feedback"
+ namespace: "wear_frameworks"
+ name: "enable_scroll_feedback_for_touch"
description: "Enables touchscreen haptic scroll feedback"
- bug: "331830899"
+ bug: "382135785"
is_fixed_read_only: true
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5dd29b26730d..f82e5f984f5d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -946,11 +946,16 @@ public final class InputMethodManager {
if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
// when losing focus (e.g., by going to another window), we reset the
// requestedVisibleTypes of WindowInsetsController by hiding the IME
+ final var statsToken = ImeTracker.forLogging().onStart(
+ ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
+ false /* fromUser */);
if (DEBUG) {
Log.d(TAG, "onWindowLostFocus, hiding IME because "
+ "of STATE_ALWAYS_HIDDEN");
}
- mCurRootView.getInsetsController().hide(WindowInsets.Type.ime());
+ mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
}
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index b606340b77a7..b9293242e4ff 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -16,13 +16,20 @@
package android.view.textclassifier;
+import static android.Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ServiceManager;
+import android.permission.flags.Flags;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
@@ -115,6 +122,29 @@ public final class TextClassificationManager {
}
}
+ /**
+ * Returns a specific type of text classifier.
+ * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
+ * <p>
+ *
+ * @see TextClassifier#CLASSIFIER_TYPE_SELF_PROVIDED
+ * @see TextClassifier#CLASSIFIER_TYPE_DEVICE_DEFAULT
+ * @see TextClassifier#CLASSIFIER_TYPE_ANDROID_DEFAULT
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @RequiresPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
+ public TextClassifier getClassifier(@TextClassifierType int type) {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Caller does not have permission " + ACCESS_TEXT_CLASSIFIER_BY_TYPE);
+ }
+ return getTextClassifier(type);
+ }
+
private TextClassificationConstants getSettings() {
synchronized (mLock) {
if (mSettings == null) {
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ef5004536354..59afdac1bfd7 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,16 +16,20 @@
package android.view.textclassifier;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.permission.flags.Flags;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.URLSpan;
@@ -63,11 +67,6 @@ public interface TextClassifier {
/** @hide */
String LOG_TAG = "androidtc";
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SYSTEM})
- @interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
@@ -76,8 +75,33 @@ public interface TextClassifier {
int DEFAULT_SYSTEM = 2;
/** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {CLASSIFIER_TYPE_SELF_PROVIDED, CLASSIFIER_TYPE_DEVICE_DEFAULT,
+ CLASSIFIER_TYPE_ANDROID_DEFAULT})
+ @interface TextClassifierType {
+ }
+ /** Specifies a TextClassifier that runs locally in the app's process. @hide */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_SELF_PROVIDED = LOCAL;
+ /**
+ * Specifies a TextClassifier that is set as the default on this particular device. This may be
+ * the same as CLASSIFIER_TYPE_DEVICE_DEFAULT, unless set otherwise by the device manufacturer.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_DEVICE_DEFAULT = SYSTEM;
+ /** Specifies the TextClassifier that is provided by Android. @hide */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_ANDROID_DEFAULT = DEFAULT_SYSTEM;
+
+ /** @hide */
+ @SuppressLint("SwitchIntDef")
static String typeToString(@TextClassifierType int type) {
- switch (type) {
+ int unflaggedType = type;
+ switch (unflaggedType) {
case LOCAL:
return "Local";
case SYSTEM:
@@ -108,6 +132,9 @@ public interface TextClassifier {
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /** Onetime password. */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ String TYPE_OTP = "otp";
/**
* Word that users may be interested to look up for meaning.
* @hide
@@ -126,7 +153,8 @@ public interface TextClassifier {
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
- TYPE_DICTIONARY
+ TYPE_DICTIONARY,
+ TYPE_OTP
})
@interface EntityType {}
@@ -198,6 +226,16 @@ public interface TextClassifier {
String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
/**
+ * Extra specifying the package name of the app from which the text to be classified originated.
+ *
+ * For example, a notification assistant might use TextClassifier, but the notification
+ * content could originate from a different app. This key allows you to provide
+ * the package name of that source app.
+ */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ String EXTRA_TEXT_ORIGIN_PACKAGE = "android.view.textclassifier.extra.TEXT_ORIGIN_PACKAGE";
+
+ /**
* Returns suggested text selection start and end indices, recognized entity types, and their
* associated confidence scores. The entity types are ordered from highest to lowest scoring.
*
diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags
index a90aebd71716..8bbd5a9d0246 100644
--- a/core/java/android/webkit/EventLogTags.logtags
+++ b/core/java/android/webkit/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.webkit;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3c854ea4fad4..fc3014a0eaec 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,7 +16,7 @@
package android.widget;
-import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.enableScrollFeedbackForTouch;
import static android.view.flags.Flags.scrollFeedbackApi;
import static android.view.flags.Flags.viewVelocityApi;
@@ -3737,7 +3737,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
// TODO: b/360198915 - Add unit testing for using ScrollFeedbackProvider
- if (enableTouchScrollFeedback()) {
+ if (vtev != null && enableScrollFeedbackForTouch()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
vtev.getDeviceId(), vtev.getSource(), MotionEvent.AXIS_Y,
@@ -3779,7 +3779,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mTouchMode = TOUCH_MODE_OVERSCROLL;
}
- if (enableTouchScrollFeedback()) {
+ if (vtev != null && enableScrollFeedbackForTouch()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
vtev.getDeviceId(), vtev.getSource(),
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2cd390113040..9fe3fd6ddc1a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,6 +20,7 @@ import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL;
import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO;
import static android.appwidget.flags.Flags.drawDataParcel;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
+import static android.content.res.Flags.FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO;
import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
@@ -47,6 +48,7 @@ import android.app.LoadedApk;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager.ServiceCollectionCache;
import android.appwidget.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -54,7 +56,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.ServiceConnection;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
@@ -82,7 +83,6 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -127,8 +127,8 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.IRemoteViewsFactory;
-import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
@@ -1391,8 +1391,10 @@ public class RemoteViews implements Parcelable, Filter {
/**
* @hide
*/
- public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
- return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
+ public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit,
+ @NonNull ServiceCollectionCache collectionCache) {
+ return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit,
+ collectionCache);
}
private class RemoteCollectionCache {
@@ -1446,7 +1448,8 @@ public class RemoteViews implements Parcelable, Filter {
}
public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
- @NonNull RemoteViews inViews, int bitmapSizeLimit) {
+ @NonNull RemoteViews inViews, int bitmapSizeLimit,
+ @NonNull ServiceCollectionCache collectionCache) {
SparseArray<Intent> idToIntentMapping = new SparseArray<>();
// Collect the number of uinque Intent (which is equal to the number of new connections
// to make) for size allocation and exclude certain collections from being written to
@@ -1478,7 +1481,7 @@ public class RemoteViews implements Parcelable, Filter {
/ numOfIntents;
return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
- idToIntentMapping);
+ idToIntentMapping, collectionCache);
}
private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1544,13 +1547,14 @@ public class RemoteViews implements Parcelable, Filter {
}
private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
- int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
+ int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping,
+ @NonNull ServiceCollectionCache collectionCache) {
List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
for (int i = 0; i < idToIntentMapping.size(); i++) {
String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
Intent currentIntent = idToIntentMapping.valueAt(i);
intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
- individualSize, individualBitmapSize)
+ individualSize, individualBitmapSize, collectionCache)
.thenAccept(items -> {
items.setHierarchyRootData(getHierarchyRootData());
mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1561,7 +1565,8 @@ public class RemoteViews implements Parcelable, Filter {
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent, int individualSize, int individualBitmapSize) {
+ Intent intent, int individualSize, int individualBitmapSize,
+ @NonNull ServiceCollectionCache collectionCache) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1581,39 +1586,24 @@ public class RemoteViews implements Parcelable, Filter {
return result;
}
- context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
- result.defaultExecutor(), new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName,
- IBinder iBinder) {
- RemoteCollectionItems items;
- try {
- items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems(individualSize,
- individualBitmapSize);
- } catch (RemoteException re) {
- items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the"
- + " factory", re);
- } finally {
- context.unbindService(this);
- }
-
- if (items == null) {
- items = new RemoteCollectionItems.Builder().build();
- }
-
- result.complete(items);
- }
+ collectionCache.connectAndConsume(intent, iBinder -> {
+ RemoteCollectionItems items;
+ try {
+ items = IRemoteViewsFactory.Stub.asInterface(iBinder)
+ .getRemoteCollectionItems(individualSize,
+ individualBitmapSize);
+ } catch (RemoteException re) {
+ items = new RemoteCollectionItems.Builder().build();
+ Log.e(LOG_TAG, "Error getting collection items from the"
+ + " factory", re);
+ }
- @Override
- public void onNullBinding(ComponentName name) {
- context.unbindService(this);
- }
+ if (items == null) {
+ items = new RemoteCollectionItems.Builder().build();
+ }
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
- });
+ result.complete(items);
+ }, result.defaultExecutor());
result.completeOnTimeout(
new RemoteCollectionItems.Builder().build(),
@@ -8709,6 +8699,7 @@ public class RemoteViews implements Parcelable, Filter {
*
* @hide
*/
+ @FlaggedApi(FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO)
@Nullable
public static ColorResources createWithOverlay(Context context,
SparseIntArray colorMapping) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 511c832a4876..184933fb8288 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,7 +16,7 @@
package android.widget;
-import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.enableScrollFeedbackForTouch;
import static android.view.flags.Flags.viewVelocityApi;
import android.annotation.ColorInt;
@@ -909,7 +909,7 @@ public class ScrollView extends FrameLayout {
}
// TODO: b/360198915 - Add unit tests.
- if (enableTouchScrollFeedback()) {
+ if (enableScrollFeedbackForTouch()) {
if (hitTopLimit || hitBottomLimit) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(vtev.getDeviceId(),
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d7750bd412a3..71a832d84f08 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -18,6 +18,7 @@ package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.graphics.Paint.NEW_FONT_VARIATION_MANAGEMENT;
import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.ContentInfo.SOURCE_CLIPBOARD;
@@ -5542,7 +5543,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& fontVariationSettings.equals(existingSettings))) {
return true;
}
- boolean effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
+
+ final boolean useFontVariationStore = Flags.typefaceRedesignReadonly()
+ && CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
+ boolean effective;
+ if (useFontVariationStore) {
+ if (mFontWeightAdjustment != 0
+ && mFontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) {
+ mTextPaint.setFontVariationSettings(fontVariationSettings, mFontWeightAdjustment);
+ } else {
+ mTextPaint.setFontVariationSettings(fontVariationSettings);
+ }
+ effective = true;
+ } else {
+ effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
+ }
if (effective && mLayout != null) {
nullLayouts();
@@ -10114,6 +10129,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
outAttrs.extras.putBoolean(
STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY, handwritingEnabled);
}
+ if (android.view.inputmethod.Flags.writingTools()) {
+ // default to same behavior as isSuggestionsEnabled().
+ outAttrs.setWritingToolsEnabled(isSuggestionsEnabled());
+ }
ArrayList<Class<? extends HandwritingGesture>> gestures = new ArrayList<>();
gestures.add(SelectGesture.class);
gestures.add(SelectRangeGesture.class);
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index b535effd393a..f61eb30d3171 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -52,6 +52,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
*/
private static final float SCALE_FACTOR = 100f;
private static final float FLING_FRICTION = 8f;
+ private static final float BUTTON_SPRING_STIFFNESS = 100;
private final SpringAnimation mSpring;
private ProgressCallback mCallback;
private float mProgress = 0;
@@ -156,7 +157,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
/* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS);
if (predictiveBackSwipeEdgeNoneApi()) {
if (event.getSwipeEdge() == EDGE_NONE) {
- mButtonSpringForce.setStiffness(SpringForce.STIFFNESS_LOW);
+ mButtonSpringForce.setStiffness(BUTTON_SPRING_STIFFNESS);
mSpring.setSpring(mButtonSpringForce);
mSpring.animateToFinalPosition(SCALE_FACTOR);
} else {
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 544642811a39..fc41307c4d1f 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
+import android.graphics.Gainmap;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -184,17 +185,29 @@ public class ScreenCapture {
* @hide
*/
public static class ScreenshotHardwareBuffer {
+ private static final float EPSILON = 1.0f / 64.0f;
+
private final HardwareBuffer mHardwareBuffer;
private final ColorSpace mColorSpace;
private final boolean mContainsSecureLayers;
private final boolean mContainsHdrLayers;
+ private final HardwareBuffer mGainmap;
+ private final float mHdrSdrRatio;
public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
boolean containsSecureLayers, boolean containsHdrLayers) {
+ this(hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers, null, 1.0f);
+ }
+
+ public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
+ boolean containsSecureLayers, boolean containsHdrLayers, HardwareBuffer gainmap,
+ float hdrSdrRatio) {
mHardwareBuffer = hardwareBuffer;
mColorSpace = colorSpace;
mContainsSecureLayers = containsSecureLayers;
mContainsHdrLayers = containsHdrLayers;
+ mGainmap = gainmap;
+ mHdrSdrRatio = hdrSdrRatio;
}
/**
@@ -209,13 +222,12 @@ public class ScreenCapture {
* @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
*/
private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
- int dataspace, boolean containsSecureLayers, boolean containsHdrLayers) {
+ int dataspace, boolean containsSecureLayers, boolean containsHdrLayers,
+ HardwareBuffer gainmap, float hdrSdrRatio) {
ColorSpace colorSpace = ColorSpace.getFromDataSpace(dataspace);
- return new ScreenshotHardwareBuffer(
- hardwareBuffer,
+ return new ScreenshotHardwareBuffer(hardwareBuffer,
colorSpace != null ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB),
- containsSecureLayers,
- containsHdrLayers);
+ containsSecureLayers, containsHdrLayers, gainmap, hdrSdrRatio);
}
public ColorSpace getColorSpace() {
@@ -259,7 +271,22 @@ public class ScreenCapture {
Log.w(TAG, "Failed to take screenshot. Null screenshot object");
return null;
}
- return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+
+ Bitmap bitmap = Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+ if (mGainmap != null) {
+ Bitmap gainmapBitmap = Bitmap.wrapHardwareBuffer(mGainmap, null);
+ Gainmap gainmap = new Gainmap(gainmapBitmap);
+ gainmap.setRatioMin(1.0f, 1.0f, 1.0f);
+ gainmap.setRatioMax(mHdrSdrRatio, mHdrSdrRatio, mHdrSdrRatio);
+ gainmap.setGamma(1.0f, 1.0f, 1.0f);
+ gainmap.setEpsilonSdr(EPSILON, EPSILON, EPSILON);
+ gainmap.setEpsilonHdr(EPSILON, EPSILON, EPSILON);
+ gainmap.setMinDisplayRatioForHdrTransition(1.0f);
+ gainmap.setDisplayRatioForFullHdr(mHdrSdrRatio);
+ bitmap.setGainmap(gainmap);
+ }
+
+ return bitmap;
}
}
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index a37bef80ff04..53c64bd6e664 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -77,6 +77,8 @@ public class TaskSnapshot implements Parcelable {
private final ColorSpace mColorSpace;
private int mInternalReferences;
+ /** Keep in cache, doesn't need reference. */
+ public static final int REFERENCE_NONE = 0;
/** This snapshot object is being broadcast. */
public static final int REFERENCE_BROADCAST = 1;
/** This snapshot object is in the cache. */
@@ -85,11 +87,16 @@ public class TaskSnapshot implements Parcelable {
public static final int REFERENCE_PERSIST = 1 << 2;
/** This snapshot object is being used for content suggestion. */
public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3;
+ /** This snapshot object will be passing to external process. Keep the snapshot reference after
+ * writeToParcel*/
+ public static final int REFERENCE_WRITE_TO_PARCEL = 1 << 4;
@IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
+ REFERENCE_NONE,
REFERENCE_BROADCAST,
REFERENCE_CACHE,
REFERENCE_PERSIST,
- REFERENCE_CONTENT_SUGGESTION
+ REFERENCE_CONTENT_SUGGESTION,
+ REFERENCE_WRITE_TO_PARCEL
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReferenceFlags {}
@@ -309,6 +316,11 @@ public class TaskSnapshot implements Parcelable {
dest.writeBoolean(mIsTranslucent);
dest.writeBoolean(mHasImeSurface);
dest.writeInt(mUiMode);
+ synchronized (this) {
+ if ((mInternalReferences & REFERENCE_WRITE_TO_PARCEL) != 0) {
+ removeReference(REFERENCE_WRITE_TO_PARCEL);
+ }
+ }
}
@Override
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index fe936f77de07..f42c0ec5ee7c 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -44,10 +44,10 @@ public final class TransitionRequestInfo implements Parcelable {
private @Nullable ActivityManager.RunningTaskInfo mTriggerTask;
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
- private @Nullable ActivityManager.RunningTaskInfo mPipTask;
+ private @Nullable TransitionRequestInfo.PipChange mPipChange;
/** If non-null, a remote-transition associated with the source of this transition. */
private @Nullable RemoteTransition mRemoteTransition;
@@ -70,7 +70,7 @@ public final class TransitionRequestInfo implements Parcelable {
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
- this(type, triggerTask, null /* pipTask */,
+ this(type, triggerTask, null /* pipChange */,
remoteTransition, null /* displayChange */, 0 /* flags */, -1 /* debugId */);
}
@@ -80,7 +80,7 @@ public final class TransitionRequestInfo implements Parcelable {
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
int flags) {
- this(type, triggerTask, null /* pipTask */,
+ this(type, triggerTask, null /* pipChange */,
remoteTransition, null /* displayChange */, flags, -1 /* debugId */);
}
@@ -91,7 +91,7 @@ public final class TransitionRequestInfo implements Parcelable {
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags,
+ this(type, triggerTask, null /* pipChange */, remoteTransition, displayChange, flags,
-1 /* debugId */);
}
@@ -103,7 +103,9 @@ public final class TransitionRequestInfo implements Parcelable {
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, pipTask, remoteTransition, displayChange, flags, -1 /* debugId */);
+ this(type, triggerTask,
+ pipTask != null ? new TransitionRequestInfo.PipChange(pipTask) : null,
+ remoteTransition, displayChange, flags, -1 /* debugId */);
}
/** @hide */
@@ -252,7 +254,7 @@ public final class TransitionRequestInfo implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- protected DisplayChange(@android.annotation.NonNull android.os.Parcel in) {
+ /* package-private */ DisplayChange(@android.annotation.NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -289,7 +291,7 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1697564781403L,
+ time = 1733334462577L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -302,6 +304,143 @@ public final class TransitionRequestInfo implements Parcelable {
}
+ @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
+ public static final class PipChange implements Parcelable {
+ // In AE case, we might care about the TF token instead of the task token.
+ @android.annotation.NonNull
+ private WindowContainerToken mTaskFragmentToken;
+
+ @android.annotation.NonNull
+ private ActivityManager.RunningTaskInfo mTaskInfo;
+
+ /** Create empty display-change. */
+ public PipChange(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskFragmentToken = taskInfo.token;
+ mTaskInfo = taskInfo;
+ }
+
+ /** Create a display-change representing a rotation. */
+ public PipChange(WindowContainerToken taskFragmentToken,
+ ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskFragmentToken = taskFragmentToken;
+ mTaskInfo = taskInfo;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/TransitionRequestInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull WindowContainerToken getTaskFragmentToken() {
+ return mTaskFragmentToken;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull PipChange setTaskFragmentToken(@android.annotation.NonNull WindowContainerToken value) {
+ mTaskFragmentToken = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskFragmentToken);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull PipChange setTaskInfo(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
+ mTaskInfo = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskInfo);
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "PipChange { " +
+ "taskFragmentToken = " + mTaskFragmentToken + ", " +
+ "taskInfo = " + mTaskInfo +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mTaskFragmentToken, flags);
+ dest.writeTypedObject(mTaskInfo, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ PipChange(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ WindowContainerToken taskFragmentToken = (WindowContainerToken) in.readTypedObject(WindowContainerToken.CREATOR);
+ ActivityManager.RunningTaskInfo taskInfo = (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+
+ this.mTaskFragmentToken = taskFragmentToken;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskFragmentToken);
+ this.mTaskInfo = taskInfo;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskInfo);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<PipChange> CREATOR
+ = new Parcelable.Creator<PipChange>() {
+ @Override
+ public PipChange[] newArray(int size) {
+ return new PipChange[size];
+ }
+
+ @Override
+ public PipChange createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new PipChange(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1733334462588L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
+ inputSignatures = "private @android.annotation.NonNull android.window.WindowContainerToken mTaskFragmentToken\nprivate @android.annotation.NonNull android.app.ActivityManager.RunningTaskInfo mTaskInfo\nclass PipChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+ }
+
@@ -326,9 +465,9 @@ public final class TransitionRequestInfo implements Parcelable {
* @param triggerTask
* If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
- * @param pipTask
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * @param pipChange
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -344,7 +483,7 @@ public final class TransitionRequestInfo implements Parcelable {
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
- @Nullable ActivityManager.RunningTaskInfo pipTask,
+ @Nullable TransitionRequestInfo.PipChange pipChange,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags,
@@ -353,7 +492,7 @@ public final class TransitionRequestInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
- this.mPipTask = pipTask;
+ this.mPipChange = pipChange;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -380,12 +519,12 @@ public final class TransitionRequestInfo implements Parcelable {
}
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
@DataClass.Generated.Member
- public @Nullable ActivityManager.RunningTaskInfo getPipTask() {
- return mPipTask;
+ public @Nullable TransitionRequestInfo.PipChange getPipChange() {
+ return mPipChange;
}
/**
@@ -433,12 +572,12 @@ public final class TransitionRequestInfo implements Parcelable {
}
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
@DataClass.Generated.Member
- public @android.annotation.NonNull TransitionRequestInfo setPipTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
- mPipTask = value;
+ public @android.annotation.NonNull TransitionRequestInfo setPipChange(@android.annotation.NonNull TransitionRequestInfo.PipChange value) {
+ mPipChange = value;
return this;
}
@@ -471,10 +610,10 @@ public final class TransitionRequestInfo implements Parcelable {
return "TransitionRequestInfo { " +
"type = " + typeToString() + ", " +
"triggerTask = " + mTriggerTask + ", " +
- "pipTask = " + mPipTask + ", " +
+ "pipChange = " + mPipChange + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
- "flags = " + Integer.toHexString(mFlags) + ", " +
+ "flags = " + mFlags + ", " +
"debugId = " + mDebugId +
" }";
}
@@ -487,13 +626,13 @@ public final class TransitionRequestInfo implements Parcelable {
byte flg = 0;
if (mTriggerTask != null) flg |= 0x2;
- if (mPipTask != null) flg |= 0x4;
+ if (mPipChange != null) flg |= 0x4;
if (mRemoteTransition != null) flg |= 0x8;
if (mDisplayChange != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeInt(mType);
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
- if (mPipTask != null) dest.writeTypedObject(mPipTask, flags);
+ if (mPipChange != null) dest.writeTypedObject(mPipChange, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
@@ -514,7 +653,7 @@ public final class TransitionRequestInfo implements Parcelable {
byte flg = in.readByte();
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
- ActivityManager.RunningTaskInfo pipTask = (flg & 0x4) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ TransitionRequestInfo.PipChange pipChange = (flg & 0x4) == 0 ? null : (TransitionRequestInfo.PipChange) in.readTypedObject(TransitionRequestInfo.PipChange.CREATOR);
RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
@@ -524,7 +663,7 @@ public final class TransitionRequestInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
- this.mPipTask = pipTask;
+ this.mPipChange = pipChange;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -548,10 +687,10 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1697564781438L,
+ time = 1733334462604L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.PipChange mPipChange\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index a88a17283482..c8001198bdf6 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -486,7 +486,7 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
+ * Sets two containers adjacent to each other. Containers below two visible adjacent roots will
* be made invisible. This currently only applies to TaskFragment containers created by
* organizer.
* @param root1 the first root.
@@ -495,9 +495,64 @@ public final class WindowContainerTransaction implements Parcelable {
@NonNull
public WindowContainerTransaction setAdjacentRoots(
@NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
- mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
- root1.asBinder(),
- root2.asBinder()));
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
+ root1.asBinder(),
+ root2.asBinder()));
+ return this;
+ }
+ return setAdjacentRootSet(root1, root2);
+ }
+
+ /**
+ * Sets multiple containers adjacent to each other. Containers below the visible adjacent roots
+ * will be made invisible. This currently only applies to Task containers created by organizer.
+ *
+ * To remove one container from the adjacent roots, one can call {@link #clearAdjacentRoots}
+ * with the target container.
+ * To remove all containers from the adjacent roots, one much call {@link #clearAdjacentRoots}
+ * on each container if there were more than two containers in the set.
+ *
+ * For non-Task TaskFragment, use {@link #setAdjacentTaskFragments} instead.
+ *
+ * @param roots the Tasks that should be adjacent to each other.
+ * @throws IllegalArgumentException if roots have size < 2.
+ * @hide // TODO(b/373709676) Rename to setAdjacentRoots and update CTS.
+ */
+ @NonNull
+ public WindowContainerTransaction setAdjacentRootSet(
+ @NonNull WindowContainerToken... roots) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalArgumentException("allowMultipleAdjacentTaskFragments is not enabled."
+ + " Use #setAdjacentRoots instead.");
+ }
+ if (roots.length < 2) {
+ throw new IllegalArgumentException("setAdjacentRootSet must have size >= 2");
+ }
+ final IBinder[] rootTokens = new IBinder[roots.length];
+ for (int i = 0; i < roots.length; i++) {
+ rootTokens[i] = roots[i].asBinder();
+ }
+ mHierarchyOps.add(
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
+ .setContainers(rootTokens)
+ .build());
+ return this;
+ }
+
+ /**
+ * Clears container adjacent.
+ * If {@link #setAdjacentRootSet} is called with more than 2 roots, calling this will only
+ * remove the given root from the adjacent set. The rest of roots will stay adjacent to each
+ * other.
+ *
+ * @param root the root container to clear the adjacent roots for.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction clearAdjacentRoots(
+ @NonNull WindowContainerToken root) {
+ mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder()));
return this;
}
@@ -967,18 +1022,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Clears container adjacent.
- * @param root the root container to clear the adjacent roots for.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction clearAdjacentRoots(
- @NonNull WindowContainerToken root) {
- mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder()));
- return this;
- }
-
- /**
* Sets/removes the reparent leaf task flag for this {@code windowContainer}.
* When this is set, the server side will try to reparent the leaf task to task display area
* if there is an existing activity in history during the activity launch. This operation only
@@ -1520,6 +1563,9 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable
private IBinder mContainer;
+ @Nullable
+ private IBinder[] mContainers;
+
// If this is same as mContainer, then only change position, don't reparent.
@Nullable
private IBinder mReparent;
@@ -1704,6 +1750,7 @@ public final class WindowContainerTransaction implements Parcelable {
public HierarchyOp(@NonNull HierarchyOp copy) {
mType = copy.mType;
mContainer = copy.mContainer;
+ mContainers = copy.mContainers;
mBounds = copy.mBounds;
mIncludingParents = copy.mIncludingParents;
mReparent = copy.mReparent;
@@ -1729,6 +1776,7 @@ public final class WindowContainerTransaction implements Parcelable {
protected HierarchyOp(Parcel in) {
mType = in.readInt();
mContainer = in.readStrongBinder();
+ mContainers = in.createBinderArray();
mBounds = in.readTypedObject(Rect.CREATOR);
mIncludingParents = in.readBoolean();
mReparent = in.readStrongBinder();
@@ -1780,6 +1828,13 @@ public final class WindowContainerTransaction implements Parcelable {
}
@NonNull
+ public IBinder[] getContainers() {
+ return mContainers;
+ }
+
+ /** @deprecated b/373709676 replace with {@link #getContainers()}. */
+ @Deprecated
+ @NonNull
public IBinder getAdjacentRoot() {
return mReparent;
}
@@ -1869,7 +1924,7 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_REORDER: return "reorder";
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "childrenTasksReparent";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "setLaunchRoot";
- case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "setAdjacentRoot";
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "setAdjacentRoots";
case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "launchTask";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "setAdjacentFlagRoot";
case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
@@ -1883,7 +1938,7 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop";
case HIERARCHY_OP_TYPE_REMOVE_TASK: return "removeTask";
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity";
- case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoot";
+ case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoots";
case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
return "setReparentLeafTaskIfRelaunch";
case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
@@ -1923,8 +1978,18 @@ public final class WindowContainerTransaction implements Parcelable {
sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom");
break;
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
- sb.append("container=").append(mContainer)
- .append(" adjacentRoot=").append(mReparent);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ for (IBinder container : mContainers) {
+ if (container == mContainers[0]) {
+ sb.append("adjacentRoots=").append(container);
+ } else {
+ sb.append(", ").append(container);
+ }
+ }
+ } else {
+ sb.append("container=").append(mContainer)
+ .append(" adjacentRoot=").append(mReparent);
+ }
break;
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
sb.append(mLaunchOptions);
@@ -1997,6 +2062,7 @@ public final class WindowContainerTransaction implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeStrongBinder(mContainer);
+ dest.writeBinderArray(mContainers);
dest.writeTypedObject(mBounds, flags);
dest.writeBoolean(mIncludingParents);
dest.writeStrongBinder(mReparent);
@@ -2044,6 +2110,9 @@ public final class WindowContainerTransaction implements Parcelable {
private IBinder mContainer;
@Nullable
+ private IBinder[] mContainers;
+
+ @Nullable
private IBinder mReparent;
@Nullable
@@ -2104,6 +2173,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setContainers(@Nullable IBinder[] containers) {
+ mContainers = containers;
+ return this;
+ }
+
Builder setReparentContainer(@Nullable IBinder reparentContainer) {
mReparent = reparentContainer;
return this;
@@ -2209,6 +2283,7 @@ public final class WindowContainerTransaction implements Parcelable {
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
hierarchyOp.mContainer = mContainer;
+ hierarchyOp.mContainers = mContainers;
hierarchyOp.mReparent = mReparent;
hierarchyOp.mWindowingModes = mWindowingModes != null
? Arrays.copyOf(mWindowingModes, mWindowingModes.length)
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 2b370b9797e5..84a8b8f5b5b0 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -32,6 +32,7 @@ import android.view.Display;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.lang.ref.Reference;
@@ -95,20 +96,20 @@ public class WindowContext extends ContextWrapper implements WindowProvider {
}
/**
- * Updates this context to a new displayId.
+ * Moves this context to another display.
* <p>
- * Note that this doesn't re-parent previously attached windows (they should be removed and
- * re-added manually after this is called). Resources associated with this context will have
- * the correct value and configuration for the new display after this is called.
+ * Note that this re-parents all the previously attached windows. Resources associated with this
+ * context will have the correct value and configuration for the new display after this is
+ * called.
*/
- @Override
- public void updateDisplay(int displayId) {
- if (displayId == getDisplayId()) {
- return;
+ public void reparentToDisplay(int displayId) {
+ if (Flags.reparentWindowTokenApi()) {
+ if (displayId == getDisplayId()) {
+ return;
+ }
+ super.updateDisplay(displayId);
+ mController.reparentToDisplayArea(mType, displayId, mOptions);
}
- super.updateDisplay(displayId);
- mController.detachIfNeeded();
- mController.attachToDisplayArea(mType, displayId, mOptions);
}
@Override
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index c9ac245bc36f..1e2f454adeef 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -158,6 +158,21 @@ public class WindowContextController {
}
}
+ /**
+ * Reparents the window context from the current attached display to another. {@code type} and
+ * {@code options} must be the same as the previous attach call, otherwise this will fail
+ * silently.
+ */
+ public void reparentToDisplayArea(
+ @WindowType int type, int displayId, @Nullable Bundle options) {
+ if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
+ attachToDisplayArea(type, displayId, options);
+ return;
+ }
+ // No need to propagate type and options as this is already attached and they can't change.
+ getWindowTokenClientController().reparentToDisplayArea(mToken, displayId);
+ }
+
/** Gets the {@link WindowTokenClientController}. */
@VisibleForTesting
@NonNull
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 6e76d8d345b2..a551fe701c5b 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -166,7 +166,7 @@ public class WindowTokenClient extends Binder {
@VisibleForTesting
public void onConfigurationChangedInner(@NonNull Context context,
@NonNull Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
final int diff;
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index fa345956ec4d..1ec05b65861d 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -197,6 +197,21 @@ public class WindowTokenClientController {
}
}
+ /**
+ * Reparents a {@link WindowTokenClient} and its associated WindowContainer if there's one.
+ */
+ public void reparentToDisplayArea(@NonNull WindowTokenClient client, int displayId) {
+ try {
+ if (!getWindowManagerService().reparentWindowContextToDisplayArea(mAppThread, client,
+ displayId)) {
+ Log.e(TAG,
+ "Didn't succeed reparenting of " + client + " to displayId=" + displayId);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
@NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
recordWindowContextToken(client);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 5053aa8a3750..1707e61b28e4 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -175,13 +175,6 @@ flag {
}
flag {
- name: "enable_a11y_metrics"
- namespace: "lse_desktop_experience"
- description: "Whether to enable log collection for a11y actions in desktop windowing mode"
- bug: "341319597"
-}
-
-flag {
name: "enable_caption_compat_inset_force_consumption"
namespace: "lse_desktop_experience"
description: "Enables force-consumption of caption bar insets for immersive apps in freeform"
@@ -414,6 +407,16 @@ flag {
}
flag {
+ name: "enable_desktop_recents_transitions_corners_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables rounded corners bugfix for Recents transitions."
+ bug: "383079261"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_move_to_next_display_shortcut"
namespace: "lse_desktop_experience"
description: "Add new keyboard shortcut of moving a task into next display"
@@ -449,6 +452,14 @@ flag {
}
flag {
+ name: "reparent_window_token_api"
+ namespace: "lse_desktop_experience"
+ description: "Allows to reparent a window token to a different display"
+ is_fixed_read_only: true
+ bug: "381258683"
+}
+
+flag {
name: "enable_desktop_windowing_hsum"
namespace: "lse_desktop_experience"
description: "Enables HSUM on desktop mode."
@@ -474,4 +485,29 @@ flag {
namespace: "lse_desktop_experience"
description: "Enable window drag between connected displays."
bug: "381172172"
+}
+
+flag {
+ name: "enable_bug_fixes_for_secondary_display"
+ namespace: "lse_desktop_experience"
+ description: "Bugfixes / papercuts to bring Desktop Windowing to secondary displays."
+ bug: "382023296"
+}
+
+flag {
+ name: "enable_top_visible_root_task_per_user_tracking"
+ namespace: "lse_desktop_experience"
+ description: "Enables tracking the top visible root tasks for a user."
+ bug: "381038076"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_per_display_desktop_wallpaper_activity"
+ namespace: "lse_desktop_experience"
+ description: "Enables having a DesktopWallpaperActivity at a per-display level."
+ bug: "381935663"
} \ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 30f0c7371270..30668a6c6b1d 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -9,16 +9,6 @@ flag {
}
flag {
- name: "reset_draw_state_on_client_invisible"
- namespace: "windowing_frontend"
- description: "Reset draw state if the client is notified to be invisible"
- bug: "373023636"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "wait_for_transition_on_display_switch"
namespace: "windowing_frontend"
description: "Waits for Shell transition to start before unblocking the screen after display switch"
@@ -39,16 +29,6 @@ flag {
}
flag {
- name: "blast_sync_notification_shade_on_display_switch"
- namespace: "windowing_frontend"
- description: "Make the buffer content of notification shade synchronize with display switch"
- bug: "337154331"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "respect_animation_clip"
namespace: "windowing_frontend"
description: "Fix missing clip transformation of animation"
@@ -207,16 +187,6 @@ flag {
}
flag {
- name: "filter_irrelevant_input_device_change"
- namespace: "windowing_frontend"
- description: "Recompute display configuration only for necessary input device changes"
- bug: "368461853"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "respect_non_top_visible_fixed_orientation"
namespace: "windowing_frontend"
description: "If top activity is not opaque, respect the fixed orientation of activity behind it"
@@ -455,6 +425,17 @@ flag {
}
flag {
+ name: "remove_defer_hiding_client"
+ namespace: "windowing_frontend"
+ description: "Remove mDeferHidingClient since everything is in shell-transition."
+ is_fixed_read_only: true
+ bug: "382485959"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "relative_insets"
namespace: "windowing_frontend"
description: "Support insets definition and calculation relative to task bounds."
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 92f9e6014107..5d4c40853009 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -267,7 +267,9 @@ public class AlertController {
return Flags.useWearMaterial3Ui()
&& CompatChanges.isChangeEnabled(WEAR_MATERIAL3_ALERTDIALOG)
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
- && context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault;
+ && (context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault
+ || context.getThemeResId()
+ == com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert);
}
static boolean canTextInput(View v) {
diff --git a/core/java/com/android/internal/app/EventLogTags.logtags b/core/java/com/android/internal/app/EventLogTags.logtags
index d681a8d26e8e..a18a8243305b 100644
--- a/core/java/com/android/internal/app/EventLogTags.logtags
+++ b/core/java/com/android/internal/app/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.internal.app;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index f01aa80fab4f..2cfc680a3fe8 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -163,5 +163,4 @@ interface IAppOpsService {
void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId);
List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId);
- oneway void noteOperationsInBatch(in Map batchedNoteOps);
}
diff --git a/core/java/com/android/internal/app/NfcResolverActivity.java b/core/java/com/android/internal/app/NfcResolverActivity.java
index 78427fe91088..f15dbd65832a 100644
--- a/core/java/com/android/internal/app/NfcResolverActivity.java
+++ b/core/java/com/android/internal/app/NfcResolverActivity.java
@@ -34,13 +34,13 @@ public class NfcResolverActivity extends ResolverActivity {
@Override
@SuppressWarnings("MissingSuperCall") // Called indirectly via `super_onCreate()`.
protected void onCreate(Bundle savedInstanceState) {
- if (!enableNfcMainline()) {
+ Intent intent = getIntent();
+ if (!enableNfcMainline() || intent.getExtras() == null) {
super_onCreate(savedInstanceState);
finish();
return;
}
- Intent intent = getIntent();
Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
ArrayList<ResolveInfo> rList =
intent.getParcelableArrayListExtra(
diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
index fa5cf2a396b9..5d4e6a083af4 100644
--- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java
+++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
@@ -36,6 +36,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.res.AssetManager;
+import android.content.res.Flags;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
@@ -235,17 +236,24 @@ public class OverlayManagerImpl {
Preconditions.checkArgument(!entryList.isEmpty(), "overlay entries shouldn't be empty");
final String overlayName = checkOverlayNameValid(overlayInternal.overlayName);
checkPackageName(overlayInternal.packageName);
- Preconditions.checkStringNotEmpty(overlayInternal.targetPackageName);
+ if (Flags.selfTargetingAndroidResourceFrro()) {
+ Preconditions.checkStringNotEmpty(overlayInternal.targetPackageName);
+ } else {
+ checkPackageName(overlayInternal.targetPackageName);
+ Preconditions.checkStringNotEmpty(
+ overlayInternal.targetOverlayable,
+ "Target overlayable should be neither null nor empty string.");
+ }
final ApplicationInfo applicationInfo = mContext.getApplicationInfo();
String targetPackage = null;
- if (TextUtils.equals(overlayInternal.targetPackageName, "android")) {
+ if (Flags.selfTargetingAndroidResourceFrro() && TextUtils.equals(
+ overlayInternal.targetPackageName, "android")) {
targetPackage = AssetManager.FRAMEWORK_APK_PATH;
} else {
targetPackage = Preconditions.checkStringNotEmpty(
applicationInfo.getBaseCodePath());
}
-
final Path frroPath = mBasePath.resolve(overlayName + FRRO_EXTENSION);
final Path idmapPath = mBasePath.resolve(overlayName + IDMAP_EXTENSION);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 2a5593f6d584..4d5e67ab8fde 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -299,6 +299,12 @@ public final class InputMethodDebug {
return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
case SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION:
return "CONTROL_WINDOW_INSETS_ANIMATION";
+ case SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED:
+ return "SHOW_INPUT_TARGET_CHANGED";
+ case SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED:
+ return "HIDE_INPUT_TARGET_CHANGED";
+ case SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS:
+ return "HIDE_WINDOW_LOST_FOCUS";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 429a6a267bb1..cf0580c2f021 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -91,6 +91,7 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED,
SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
+ SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
})
public @interface SoftInputShowHideReason {
/** Default, undefined reason. */
@@ -339,18 +340,6 @@ public @interface SoftInputShowHideReason {
int HIDE_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_WINDOW_LEGACY_DIRECT;
/**
- * Show soft input because the input target changed
- * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
- */
- int SHOW_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_SHOW_INPUT_TARGET_CHANGED;
-
- /**
- * Hide soft input because the input target changed by
- * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
- */
- int HIDE_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_HIDE_INPUT_TARGET_CHANGED;
-
- /**
* Show / Hide soft input by
* {@link android.inputmethodservice.InputMethodService#resetStateForNewConfiguration}.
*/
@@ -418,4 +407,19 @@ public @interface SoftInputShowHideReason {
* {@link android.view.InsetsController#controlWindowInsetsAnimation}.
*/
int CONTROL_WINDOW_INSETS_ANIMATION = ImeProtoEnums.REASON_CONTROL_WINDOW_INSETS_ANIMATION;
+
+ /**
+ * Show soft input because the input target changed
+ * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
+ */
+ int SHOW_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_SHOW_INPUT_TARGET_CHANGED;
+
+ /**
+ * Hide soft input because the input target changed by
+ * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
+ */
+ int HIDE_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_HIDE_INPUT_TARGET_CHANGED;
+
+ /** Hide soft input when the window lost focus. */
+ int HIDE_WINDOW_LOST_FOCUS = ImeProtoEnums.REASON_HIDE_WINDOW_LOST_FOCUS;
}
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
index 66ee131badac..dfec49907c69 100644
--- a/core/java/com/android/internal/jank/EventLogTags.logtags
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.internal.jank;
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
index 693bd16e6170..db47797cb03d 100644
--- a/core/java/com/android/internal/logging/EventLogTags.logtags
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.internal.logging;
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 4400ed117721..1923c5fb9186 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -20,7 +20,7 @@
"file_patterns": [
"BinderDeathDispatcher\\.java"
],
- "name": "FrameworksCoreTests_internal_os_binder"
+ "name": "FrameworksCoreTests_all_binder"
},
{
"file_patterns": [
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index 0e0098ebf074..efdc8ca694b8 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -61,7 +61,7 @@ public class MetricsLoggerWrapper {
return;
}
int pid = Process.myPid();
- String processName = Application.getProcessName();
+ String processName = Process.myProcessName();
Collection<NativeAllocationRegistry.Metrics> metrics =
NativeAllocationRegistry.getMetrics();
int nMetrics = metrics.size();
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index c160b42f8b6b..5c08dc6be1a0 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -3133,9 +3133,9 @@ public class ParsingPackageUtils {
private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser) {
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAdoptPermissions);
try {
- String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
+ String name = nonConfigString(0, R.styleable.AndroidManifestAdoptPermissions_name, sa);
if (name != null) {
pkg.addAdoptPermission(name);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index ff08dd27225f..3e2f30118b2a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,6 +67,13 @@ interface IStatusBarService
// ---- Methods below are for use by the status bar policy services ----
// You need the STATUS_BAR_SERVICE permission
RegisterStatusBarResult registerStatusBar(IStatusBar callbacks);
+ /**
+ * Registers the status bar for all displays.
+ *
+ * Returns a map of all display IDs (as strings) to their corresponding RegisterStatusBarResult
+ * objects.
+ */
+ Map<String, RegisterStatusBarResult> registerStatusBarForAllDisplays(IStatusBar callbacks);
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 1e965c5db7ae..bda7547087ae 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.util.ArraySet;
import android.util.EmptyArray;
@@ -39,6 +40,10 @@ import java.util.function.IntFunction;
/**
* Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
+ * <p>
+ * Test with:
+ * <code>atest FrameworksUtilTests:com.android.internal.util.ArrayUtilsTest</code>
+ * <code>atest FrameworksUtilTestsRavenwood:com.android.internal.util.ArrayUtilsTest</code>
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrayUtils {
@@ -85,6 +90,69 @@ public class ArrayUtils {
}
/**
+ * This is like <code>new byte[length]</code>, but it allocates the array as non-movable. This
+ * prevents copies of the data from being left on the Java heap as a result of heap compaction.
+ * Use this when the array will contain sensitive data such as a password or cryptographic key
+ * that needs to be wiped from memory when no longer needed. The owner of the array is still
+ * responsible for the zeroization; {@link #zeroize(byte[])} should be used to do so.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static byte[] newNonMovableByteArray(int length) {
+ return (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, length);
+ }
+
+ /**
+ * Like {@link #newNonMovableByteArray(int)}, but allocates a char array.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static char[] newNonMovableCharArray(int length) {
+ return (char[]) VMRuntime.getRuntime().newNonMovableArray(char.class, length);
+ }
+
+ /**
+ * Zeroizes a byte array as securely as possible. Use this when the array contains sensitive
+ * data such as a password or cryptographic key.
+ * <p>
+ * This zeroizes the array in a way that is guaranteed to not be optimized out by the compiler.
+ * If supported by the architecture, it zeroizes the data not just in the L1 data cache but also
+ * in other levels of the memory hierarchy up to and including main memory (but not above that).
+ * <p>
+ * This works on any <code>byte[]</code>, but to ensure that copies of the array aren't left on
+ * the Java heap the array should have been allocated with {@link #newNonMovableByteArray(int)}.
+ * Use on other arrays might also introduce performance anomalies.
+ *
+ * @param array the array to zeroize. If null, this method has no effect.
+ */
+ @RavenwoodReplace public static native void zeroize(byte[] array);
+
+ /**
+ * Replacement of the above method for host-side unit testing that doesn't support JNI yet.
+ */
+ public static void zeroize$ravenwood(byte[] array) {
+ if (array != null) {
+ Arrays.fill(array, (byte) 0);
+ }
+ }
+
+ /**
+ * Like {@link #zeroize(byte[])}, but for char arrays.
+ */
+ @RavenwoodReplace public static native void zeroize(char[] array);
+
+ /**
+ * Replacement of the above method for host-side unit testing that doesn't support JNI yet.
+ */
+ public static void zeroize$ravenwood(char[] array) {
+ if (array != null) {
+ Arrays.fill(array, (char) 0);
+ }
+ }
+
+ /**
* Checks if the beginnings of two byte arrays are equal.
*
* @param array1 the first byte array
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 754f77e72f8a..d49afa735646 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -435,9 +435,11 @@ public class LatencyTracker {
public void startListeningForLatencyTrackerConfigChanges() {
final Context context = ActivityThread.currentApplication();
if (context == null) {
- if (DEBUG) {
- Log.d(TAG, "No application for package: " + ActivityThread.currentPackageName());
- }
+ Log.e(
+ TAG,
+ String.format(
+ "No application for package: %s. Latency Tracker Disabled",
+ ActivityThread.currentPackageName()));
return;
}
if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index 72b5488f4bac..0ed0613d02e6 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -16,6 +16,8 @@
package com.android.internal.view;
+import static android.view.flags.Flags.scrollCaptureRelaxScrollViewCriteria;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
@@ -49,7 +51,7 @@ public class ScrollCaptureInternal {
public static final int TYPE_FIXED = 0;
/**
- * Slides a single child view using mScrollX/mScrollY.
+ * Moves the viewport across absolute positioned child views using the scrollY property.
*/
public static final int TYPE_SCROLLING = 1;
@@ -63,7 +65,7 @@ public class ScrollCaptureInternal {
/**
* Unknown scrollable view with no child views (or not a subclass of ViewGroup).
*/
- private static final int TYPE_OPAQUE = 3;
+ public static final int TYPE_OPAQUE = 3;
/**
* Performs tests on the given View and determines:
@@ -73,7 +75,7 @@ public class ScrollCaptureInternal {
* This needs to be fast and not alloc memory. It's called on everything in the tree not marked
* as excluded during scroll capture search.
*/
- private static int detectScrollingType(View view) {
+ public static int detectScrollingType(View view) {
// Confirm that it can scroll.
if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
// Nothing to scroll here, move along.
@@ -95,25 +97,25 @@ public class ScrollCaptureInternal {
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: is a subclass of ViewGroup");
}
-
- // ScrollViews accept only a single child.
- if (((ViewGroup) view).getChildCount() > 1) {
- if (DEBUG_VERBOSE) {
- Log.v(TAG, "hint: scrollable with multiple children");
+ // Flag: Optionally allow ScrollView-like ViewGroups which have more than one child view.
+ if (!scrollCaptureRelaxScrollViewCriteria()) {
+ // ScrollViews accept only a single child.
+ if (((ViewGroup) view).getChildCount() > 1) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollable with multiple children");
+ }
+ return TYPE_RECYCLING;
}
- return TYPE_RECYCLING;
}
// At least one child view is required.
- if (((ViewGroup) view).getChildCount() < 1) {
- if (DEBUG_VERBOSE) {
- Log.v(TAG, "scrollable with no children");
- }
+ if (((ViewGroup) view).getChildCount() == 0) {
+ Log.w(TAG, "scrollable but no children!");
return TYPE_OPAQUE;
}
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: single child view");
}
- //Because recycling containers don't use scrollY, a non-zero value means Scroll view.
+ // Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: scrollY != 0");
@@ -132,7 +134,7 @@ public class ScrollCaptureInternal {
Log.v(TAG, "hint: cannot be scrolled up");
}
- // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
+ // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() >= 1.
// For Recycling containers, this should be a no-op (RecyclerView logs a warning)
view.scrollTo(view.getScrollX(), 1);
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index c85257578492..3a7c75afdd14 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -30,7 +30,6 @@ import android.util.AttributeSet;
import android.view.RemotableViewMethod;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
-import android.widget.TextView;
import android.widget.flags.Flags;
import com.android.internal.R;
@@ -59,7 +58,6 @@ public class CallLayout extends FrameLayout {
private CachingIconView mConversationIconView;
private CachingIconView mIcon;
private CachingIconView mConversationIconBadgeBg;
- private TextView mConversationText;
public CallLayout(@NonNull Context context) {
super(context);
@@ -83,7 +81,6 @@ public class CallLayout extends FrameLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mPeopleHelper.init(getContext());
- mConversationText = findViewById(R.id.conversation_text);
mConversationIconView = findViewById(R.id.conversation_icon);
mIcon = findViewById(R.id.icon);
mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 4b90420a75ee..b3ab5d3cd258 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -777,37 +777,40 @@ public class ConversationLayout extends FrameLayout
}
- int conversationAvatarSize;
- int facepileAvatarSize;
- int facePileBackgroundSize;
- if (mIsCollapsed) {
- conversationAvatarSize = mConversationAvatarSize;
- facepileAvatarSize = mFacePileAvatarSize;
- facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidth;
- } else {
- conversationAvatarSize = mConversationAvatarSizeExpanded;
- facepileAvatarSize = mFacePileAvatarSizeExpandedGroup;
- facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded;
- }
- LayoutParams layoutParams = (LayoutParams) mConversationFacePile.getLayoutParams();
- layoutParams.width = conversationAvatarSize;
- layoutParams.height = conversationAvatarSize;
- mConversationFacePile.setLayoutParams(layoutParams);
+ if (!notificationsRedesignTemplates()) {
+ // We no longer need to update the size based on expansion state.
+ int conversationAvatarSize;
+ int facepileAvatarSize;
+ int facePileBackgroundSize;
+ if (mIsCollapsed) {
+ conversationAvatarSize = mConversationAvatarSize;
+ facepileAvatarSize = mFacePileAvatarSize;
+ facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidth;
+ } else {
+ conversationAvatarSize = mConversationAvatarSizeExpanded;
+ facepileAvatarSize = mFacePileAvatarSizeExpandedGroup;
+ facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded;
+ }
+ LayoutParams layoutParams = (LayoutParams) mConversationFacePile.getLayoutParams();
+ layoutParams.width = conversationAvatarSize;
+ layoutParams.height = conversationAvatarSize;
+ mConversationFacePile.setLayoutParams(layoutParams);
- layoutParams = (LayoutParams) bottomView.getLayoutParams();
- layoutParams.width = facepileAvatarSize;
- layoutParams.height = facepileAvatarSize;
- bottomView.setLayoutParams(layoutParams);
+ layoutParams = (LayoutParams) bottomView.getLayoutParams();
+ layoutParams.width = facepileAvatarSize;
+ layoutParams.height = facepileAvatarSize;
+ bottomView.setLayoutParams(layoutParams);
- layoutParams = (LayoutParams) topView.getLayoutParams();
- layoutParams.width = facepileAvatarSize;
- layoutParams.height = facepileAvatarSize;
- topView.setLayoutParams(layoutParams);
+ layoutParams = (LayoutParams) topView.getLayoutParams();
+ layoutParams.width = facepileAvatarSize;
+ layoutParams.height = facepileAvatarSize;
+ topView.setLayoutParams(layoutParams);
- layoutParams = (LayoutParams) bottomBackground.getLayoutParams();
- layoutParams.width = facePileBackgroundSize;
- layoutParams.height = facePileBackgroundSize;
- bottomBackground.setLayoutParams(layoutParams);
+ layoutParams = (LayoutParams) bottomBackground.getLayoutParams();
+ layoutParams.width = facePileBackgroundSize;
+ layoutParams.height = facePileBackgroundSize;
+ bottomBackground.setLayoutParams(layoutParams);
+ }
}
/**
@@ -832,6 +835,11 @@ public class ConversationLayout extends FrameLayout
* update the icon position and sizing
*/
private void updateIconPositionAndSize() {
+ if (notificationsRedesignTemplates()) {
+ // Icon size is fixed in the redesign.
+ return;
+ }
+
int badgeProtrusion;
int conversationAvatarSize;
if (mIsOneToOne || mIsCollapsed) {
@@ -864,6 +872,11 @@ public class ConversationLayout extends FrameLayout
}
private void updatePaddingsBasedOnContentAvailability() {
+ if (notificationsRedesignTemplates()) {
+ // group icons have the same size as 1:1 conversations
+ return;
+ }
+
// groups have avatars that need more spacing
mMessagingLinearLayout.setSpacing(
mIsOneToOne ? mMessageSpacingStandard : mMessageSpacingGroup);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 19c6f51ff9a7..9bd52372e6c4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1326,7 +1326,7 @@ public class LockPatternUtils {
try {
getLockSettings().registerStrongAuthTracker(strongAuthTracker.getStub());
} catch (RemoteException e) {
- throw new RuntimeException("Could not register StrongAuthTracker");
+ e.rethrowFromSystemServer();
}
}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 4305ba753e46..31d9770f6ac4 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -81,7 +81,6 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements
private MessagingLinearLayout mMessageContainer;
ImageFloatingTextView mSenderView;
private ImageView mAvatarView;
- private View mAvatarContainer;
private String mAvatarSymbol = "";
private int mLayoutColor;
private CharSequence mAvatarName = "";
@@ -449,6 +448,17 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements
mSenderView.setVisibility(hidden ? GONE : VISIBLE);
}
+ private void updateIconVisibility() {
+ if (Flags.notificationsRedesignTemplates() && !mIsInConversation) {
+ // We don't show any icon (other than the app icon) in the collapsed form. For
+ // conversations, keeping this container helps with aligning the message to the icon
+ // when collapsed, but the old messaging style already has this alignment built into
+ // the template like all other layouts. Conversations are special because we use the
+ // same base layout for both the collapsed and expanded views.
+ mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE);
+ }
+ }
+
@Override
public boolean hasDifferentHeightWhenFirst() {
return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName);
@@ -704,6 +714,7 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements
updateMaxDisplayedLines();
updateClipRect();
updateSenderVisibility();
+ updateIconVisibility();
}
}
@@ -719,6 +730,14 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements
public void setIsInConversation(boolean isInConversation) {
if (mIsInConversation != isInConversation) {
mIsInConversation = isInConversation;
+
+ if (Flags.notificationsRedesignTemplates()) {
+ updateIconVisibility();
+ // No other alignment adjustments are necessary in the redesign, as the size of the
+ // icons in both conversations and old messaging notifications are the same.
+ return;
+ }
+
MarginLayoutParams layoutParams =
(MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
layoutParams.width = mIsInConversation
diff --git a/core/java/com/android/internal/widget/flags.aconfig b/core/java/com/android/internal/widget/flags.aconfig
new file mode 100644
index 000000000000..f05aa4f460a5
--- /dev/null
+++ b/core/java/com/android/internal/widget/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.internal.widget.flags"
+container: "system"
+
+flag {
+ name: "hide_last_char_with_physical_input"
+ namespace: "input"
+ description: "Feature flag for changing the default of hiding the last interacted symbol when a physical input device is present"
+ bug: "339270220"
+}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 38685b652c50..5177a0360032 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -435,10 +435,15 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
refreshViewPort();
- // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
- // landscape.
- final int x = Math.clamp(contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
- mViewPortOnScreen.left, mViewPortOnScreen.right - mPopupWindow.getWidth());
+ final int x;
+ if (mPopupWindow.getWidth() > mViewPortOnScreen.width()) {
+ // Not enough space - prefer to position as far left as possible
+ x = mViewPortOnScreen.left;
+ } else {
+ // Initialize x ensuring that the toolbar isn't rendered behind the system bar insets
+ x = Math.clamp(contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+ mViewPortOnScreen.left, mViewPortOnScreen.right - mPopupWindow.getWidth());
+ }
final int y;
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 da880e1e92e2..33f93fccff58 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -697,7 +697,9 @@ public class CoreDocument {
}
}
}
+ op.markNotDirty();
op.apply(context);
+ context.incrementOpCount();
}
}
@@ -999,6 +1001,16 @@ public class CoreDocument {
private final float[] mScaleOutput = new float[2];
private final float[] mTranslateOutput = new float[2];
private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap
+ private int mLastOpCount;
+
+ /**
+ * This is the number of ops used to calculate the last frame.
+ *
+ * @return number of ops
+ */
+ public int getOpsPerFrame() {
+ return mLastOpCount;
+ }
/**
* Returns > 0 if it needs to repaint
@@ -1016,6 +1028,7 @@ public class CoreDocument {
* @param theme the theme we want to use for this document.
*/
public void paint(@NonNull RemoteContext context, int theme) {
+ context.getLastOpCount();
context.getPaintContext().clearNeedsRepaint();
context.loadFloat(RemoteContext.ID_DENSITY, context.getDensity());
context.mMode = RemoteContext.ContextMode.UNSET;
@@ -1026,21 +1039,24 @@ public class CoreDocument {
context.mRemoteComposeState = mRemoteComposeState;
context.mRemoteComposeState.setContext(context);
+ // If we have a content sizing set, we are going to take the original document
+ // dimension into account and apply scale+translate according to the RootContentBehavior
+ // rules.
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
computeScale(context.mWidth, context.mHeight, mScaleOutput);
- computeTranslate(
- context.mWidth,
- context.mHeight,
- mScaleOutput[0],
- mScaleOutput[1],
- mTranslateOutput);
+ float sw = mScaleOutput[0];
+ float sh = mScaleOutput[1];
+ computeTranslate(context.mWidth, context.mHeight, sw, sh, mTranslateOutput);
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
- context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
+ context.mPaintContext.scale(sw, sh);
+ } else {
+ // If not, we set the document width and height to be the current context width and
+ // height.
+ setWidth((int) context.mWidth);
+ setHeight((int) context.mHeight);
}
mTimeVariables.updateTime(context);
- context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, context.mWidth);
- context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, context.mHeight);
mRepaintNext = context.updateOps();
if (mRootLayoutComponent != null) {
if (context.mWidth != mRootLayoutComponent.getWidth()
@@ -1050,11 +1066,11 @@ public class CoreDocument {
if (mRootLayoutComponent.needsMeasure()) {
mRootLayoutComponent.layout(context);
}
- // TODO -- this should be specifically about applying animation, not paint
- mRootLayoutComponent.paint(context.getPaintContext());
- context.mPaintContext.reset();
- // TODO -- should be able to remove this
- mRootLayoutComponent.updateVariables(context);
+ if (mRootLayoutComponent.needsBoundsAnimation()) {
+ mRepaintNext = 1;
+ mRootLayoutComponent.clearNeedsBoundsAnimation();
+ mRootLayoutComponent.animatingBounds(context);
+ }
if (DEBUG) {
String hierarchy = mRootLayoutComponent.displayHierarchy();
System.out.println(hierarchy);
@@ -1080,6 +1096,7 @@ public class CoreDocument {
op.markNotDirty();
((VariableSupport) op).updateVariables(context);
}
+ context.incrementOpCount();
op.apply(context);
}
}
@@ -1092,6 +1109,38 @@ public class CoreDocument {
if (DEBUG && mRootLayoutComponent != null) {
System.out.println(mRootLayoutComponent.displayHierarchy());
}
+ mLastOpCount = context.getLastOpCount();
+ }
+
+ /**
+ * Get an estimated number of operations executed in a paint
+ *
+ * @return number of operations
+ */
+ public int getNumberOfOps() {
+ int count = mOperations.size();
+
+ for (Operation mOperation : mOperations) {
+ if (mOperation instanceof Component) {
+ count += getChildOps((Component) mOperation);
+ }
+ }
+ return count;
+ }
+
+ private int getChildOps(@NonNull Component base) {
+ int count = base.mList.size();
+ for (Operation mOperation : base.mList) {
+
+ if (mOperation instanceof Component) {
+ int mult = 1;
+ if (mOperation instanceof LoopOperation) {
+ mult = ((LoopOperation) mOperation).estimateIterations();
+ }
+ count += mult * getChildOps((Component) mOperation);
+ }
+ }
+ return count;
}
@NonNull
@@ -1115,6 +1164,9 @@ public class CoreDocument {
if (mOperation instanceof Component) {
Component com = (Component) mOperation;
count += addChildren(com, map, buffer);
+ } else if (mOperation instanceof LoopOperation) {
+ LoopOperation com = (LoopOperation) mOperation;
+ count += addChildren(com, map, buffer);
}
}
@@ -1152,6 +1204,35 @@ public class CoreDocument {
if (mOperation instanceof Component) {
count += addChildren((Component) mOperation, map, tmp);
}
+ if (mOperation instanceof LoopOperation) {
+ count += addChildren((LoopOperation) mOperation, map, tmp);
+ }
+ }
+ return count;
+ }
+
+ private int addChildren(
+ @NonNull LoopOperation base,
+ @NonNull HashMap<String, int[]> map,
+ @NonNull WireBuffer tmp) {
+ int count = base.mList.size();
+ for (Operation mOperation : base.mList) {
+ Class<? extends Operation> c = mOperation.getClass();
+ int[] values;
+ if (map.containsKey(c.getSimpleName())) {
+ values = map.get(c.getSimpleName());
+ } else {
+ values = new int[2];
+ map.put(c.getSimpleName(), values);
+ }
+ values[0] += 1;
+ values[1] += sizeOfComponent(mOperation, tmp);
+ if (mOperation instanceof Component) {
+ count += addChildren((Component) mOperation, map, tmp);
+ }
+ if (mOperation instanceof LoopOperation) {
+ count += addChildren((LoopOperation) mOperation, map, tmp);
+ }
}
return count;
}
@@ -1197,7 +1278,6 @@ public class CoreDocument {
* @param ctl the call back to allow evaluation of shaders
*/
public void checkShaders(RemoteContext context, ShaderControl ctl) {
- int count = 0;
for (Operation op : mOperations) {
if (op instanceof TextData) {
op.apply(context);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 11e58ba0796f..5c3df7e95a1f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -333,6 +333,7 @@ public class RemoteComposeState implements CollectionsAccess {
public void overrideColor(int id, int color) {
mColorOverride[id] = true;
mColorMap.put(id, color);
+ updateListeners(id);
}
/** Clear the color Overrides */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index c03f44bfc162..b5587ce095a2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -40,6 +40,7 @@ import java.time.ZoneOffset;
* <p>We also contain a PaintContext, so that any operation can draw as needed.
*/
public abstract class RemoteContext {
+ private static final int MAX_OP_COUNT = 100_000; // Maximum cmds per frame
protected @NonNull CoreDocument mDocument =
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
@@ -52,6 +53,7 @@ public abstract class RemoteContext {
int mDebug = 0;
+ private int mOpCount;
private int mTheme = Theme.UNSPECIFIED;
public float mWidth = 0f;
@@ -631,4 +633,23 @@ public abstract class RemoteContext {
float right,
float bottom,
int metadataId);
+
+ /** increments the count of operations executed in a pass */
+ public void incrementOpCount() {
+ mOpCount++;
+ if (mOpCount > MAX_OP_COUNT) {
+ throw new RuntimeException("Too many operations executed");
+ }
+ }
+
+ /**
+ * Get the last Op Count and clear the count.
+ *
+ * @return the number of ops executed.
+ */
+ public int getLastOpCount() {
+ int count = mOpCount;
+ mOpCount = 0;
+ return count;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index cd7ebec67a46..ea917db98e65 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -24,7 +24,7 @@ import java.time.ZoneOffset;
/** This generates the standard system variables for time. */
public class TimeVariables {
- private static final float BUILD = 0.01f;
+ private static final float BUILD = 0.02f;
/**
* This class populates all time variables in the system
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index 29124d07e20b..bb112d1cb732 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -132,9 +132,6 @@ public class ClickArea extends Operation
@Override
public void apply(@NonNull RemoteContext context) {
- if (context.getMode() != RemoteContext.ContextMode.DATA) {
- return;
- }
context.addClickArea(
mId, mContentDescription, mOutLeft, mOutTop, mOutRight, mOutBottom, mMetadata);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 6c9105dbadfe..e9aae1ebad45 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -64,6 +64,7 @@ public class Theme extends Operation implements RemoteComposeOperation {
@Override
public void apply(@NonNull RemoteContext context) {
context.setTheme(mTheme);
+ markDirty();
}
@NonNull
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index dcf1d250b2f5..e05bdf2b824d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -106,6 +106,7 @@ public class ClickModifierOperation extends PaintOperation
for (Operation op : mList) {
if (op instanceof TextData) {
op.apply(context);
+ context.incrementOpCount();
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index e95dfdaa4cf9..8a77dc3aafa5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -66,6 +66,34 @@ public class Component extends PaintOperation implements Measurable, Serializabl
protected float mZIndex = 0f;
+ private boolean mNeedsBoundsAnimation = false;
+
+ /**
+ * Mark the component as needing a bounds animation pass
+ */
+ public void markNeedsBoundsAnimation() {
+ mNeedsBoundsAnimation = true;
+ if (mParent != null && !mParent.mNeedsBoundsAnimation) {
+ mParent.markNeedsBoundsAnimation();
+ }
+ }
+
+ /**
+ * Clear the bounds animation pass flag
+ */
+ public void clearNeedsBoundsAnimation() {
+ mNeedsBoundsAnimation = false;
+ }
+
+ /**
+ * True if needs a bounds animation
+ *
+ * @return true if needs a bounds animation pass
+ */
+ public boolean needsBoundsAnimation() {
+ return mNeedsBoundsAnimation;
+ }
+
public float getZIndex() {
return mZIndex;
}
@@ -382,12 +410,40 @@ public class Component extends PaintOperation implements Measurable, Serializabl
} else {
mVisibility = m.getVisibility();
}
- setWidth(m.getW());
- setHeight(m.getH());
- setLayoutPosition(m.getX(), m.getY());
+ if (mAnimateMeasure == null) {
+ setWidth(m.getW());
+ setHeight(m.getH());
+ setLayoutPosition(m.getX(), m.getY());
+ updateComponentValues(context);
+ clearNeedsBoundsAnimation();
+ } else {
+ mAnimateMeasure.apply(context);
+ updateComponentValues(context);
+ markNeedsBoundsAnimation();
+ }
mFirstLayout = false;
}
+ /**
+ * Animate the bounds of the component as needed
+ * @param context
+ */
+ public void animatingBounds(@NonNull RemoteContext context) {
+ if (mAnimateMeasure != null) {
+ mAnimateMeasure.apply(context);
+ updateComponentValues(context);
+ markNeedsBoundsAnimation();
+ } else {
+ clearNeedsBoundsAnimation();
+ }
+ for (Operation op : mList) {
+ if (op instanceof Measurable) {
+ Measurable m = (Measurable) op;
+ m.animatingBounds(context);
+ }
+ }
+ }
+
@NonNull public float[] locationInWindow = new float[2];
public boolean contains(float x, float y) {
@@ -698,9 +754,6 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
public void paintingComponent(@NonNull PaintContext context) {
- if (!mComponentValues.isEmpty()) {
- updateComponentValues(context.getContext());
- }
if (mPreTranslate != null) {
mPreTranslate.paint(context);
}
@@ -718,8 +771,10 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
if (op instanceof PaintOperation) {
((PaintOperation) op).paint(context);
+ context.getContext().incrementOpCount();
} else {
op.apply(context.getContext());
+ context.getContext().incrementOpCount();
}
}
context.restore();
@@ -728,7 +783,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
if (context.isAnimationEnabled() && mAnimateMeasure != null) {
- mAnimateMeasure.apply(context);
+ mAnimateMeasure.paint(context);
context.needsRepaint();
return true;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index e25392c5d2ff..91038852573e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -286,7 +286,9 @@ public class LayoutComponent extends Component {
@Override
public void paintingComponent(@NonNull PaintContext context) {
Component prev = context.getContext().mLastComponent;
- context.getContext().mLastComponent = this;
+ RemoteContext remoteContext = context.getContext();
+
+ remoteContext.mLastComponent = this;
context.save();
context.translate(mX, mY);
if (context.isVisualDebug()) {
@@ -329,6 +331,7 @@ public class LayoutComponent extends Component {
child.updateVariables(context.getContext());
child.markNotDirty();
}
+ remoteContext.incrementOpCount();
child.paint(context);
}
} else {
@@ -337,6 +340,7 @@ public class LayoutComponent extends Component {
child.updateVariables(context.getContext());
child.markNotDirty();
}
+ remoteContext.incrementOpCount();
child.paint(context);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
index 505656e212f1..9fc5da8320ba 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
@@ -57,6 +57,7 @@ public abstract class ListActionsOperation extends PaintOperation
for (Operation op : mList) {
if (op instanceof TextData) {
op.apply(context);
+ context.incrementOpCount();
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
index 1b85681bcc08..ab1e0ac73368 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -107,9 +107,11 @@ public class LoopOperation extends PaintOperation implements VariableSupport {
@Override
public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
if (mIndexVariableId == 0) {
for (float i = mFromOut; i < mUntilOut; i += mStepOut) {
for (Operation op : mList) {
+ remoteContext.incrementOpCount();
op.apply(context.getContext());
}
}
@@ -120,6 +122,7 @@ public class LoopOperation extends PaintOperation implements VariableSupport {
if (op instanceof VariableSupport && op.isDirty()) {
((VariableSupport) op).updateVariables(context.getContext());
}
+ remoteContext.incrementOpCount();
op.apply(context.getContext());
}
}
@@ -172,4 +175,16 @@ public class LoopOperation extends PaintOperation implements VariableSupport {
.field(DocumentedOperation.FLOAT, "step", "value step")
.field(DocumentedOperation.FLOAT, "until", "stops less than or equal");
}
+
+ /**
+ * Calculate and estimate of the number of iterations
+ *
+ * @return number of loops or 10 if based on variables
+ */
+ public int estimateIterations() {
+ if (!(Float.isNaN(mUntil) || Float.isNaN(mFrom) || Float.isNaN(mStep))) {
+ return (int) (0.5f + (mUntil - mFrom) / mStep);
+ }
+ return 10; // this is a generic estmate if the values are variables;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index 11c0f3f394f5..11fa7ee670dd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -137,8 +137,8 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
return;
}
context.mLastComponent = this;
- mWidth = context.mWidth;
- mHeight = context.mHeight;
+ setWidth(context.mWidth);
+ setHeight(context.mHeight);
// TODO: reuse MeasurePass
MeasurePass measurePass = new MeasurePass();
@@ -155,7 +155,9 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
@Override
public void paint(@NonNull PaintContext context) {
mNeedsRepaint = false;
- context.getContext().mLastComponent = this;
+ RemoteContext remoteContext = context.getContext();
+ remoteContext.mLastComponent = this;
+
context.save();
if (mParent == null) { // root layout
@@ -165,6 +167,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
for (Operation op : mList) {
if (op instanceof PaintOperation) {
((PaintOperation) op).paint(context);
+ remoteContext.incrementOpCount();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 2af3c739035c..1de956b7e5d7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
@@ -103,13 +104,18 @@ public class AnimateMeasure {
@NonNull public PaintBundle paint = new PaintBundle();
- public void apply(@NonNull PaintContext context) {
- update(context.getContext().currentTime);
-
+ /**
+ * Apply the layout portion of the animation if any
+ *
+ * @param context
+ */
+ public void apply(@NonNull RemoteContext context) {
+ update(context.currentTime);
mComponent.setX(getX());
mComponent.setY(getY());
mComponent.setWidth(getWidth());
mComponent.setHeight(getHeight());
+ mComponent.updateVariables(context);
float w = mComponent.getWidth();
float h = mComponent.getHeight();
@@ -120,10 +126,17 @@ public class AnimateMeasure {
h -= pop.getTop() + pop.getBottom();
}
if (op instanceof DecoratorComponent) {
- ((DecoratorComponent) op).layout(context.getContext(), mComponent, w, h);
+ ((DecoratorComponent) op).layout(context, mComponent, w, h);
}
}
+ }
+ /**
+ * Paint the transition animation for the component owned
+ *
+ * @param context
+ */
+ public void paint(@NonNull PaintContext context) {
if (mOriginal.getVisibility() != mTarget.getVisibility()) {
if (mTarget.getVisibility() == Component.Visibility.GONE) {
switch (mExitAnimation) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
index fbf2784be843..a9998745a5d6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
@@ -44,4 +44,11 @@ public interface Measurable {
* @return true if need to remeasured, false otherwise
*/
boolean needsMeasure();
+
+ /**
+ * Animate bounds of the component
+ *
+ * @param context
+ */
+ void animatingBounds(RemoteContext context);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index d2ba13f69a91..a1609ace2138 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -49,6 +49,7 @@ public class ComponentModifiers extends PaintOperation
super.apply(context);
for (ModifierOperation op : mList) {
op.apply(context);
+ context.incrementOpCount();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 7dad2931c97f..6eb83f1da410 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -250,8 +250,23 @@ public class RemoteComposePlayer extends FrameLayout {
mInner.clearLocalString("SYSTEM:" + name);
}
+ /**
+ * This is the number of ops used to calculate the last frame.
+ *
+ * @return number of ops
+ */
+ public int getOpsPerFrame() {
+ return mInner.getDocument().mDocument.getOpsPerFrame();
+ }
+
/** Id action callback interface */
public interface IdActionCallbacks {
+ /**
+ * Callback for on action
+ *
+ * @param id the id of the action
+ * @param metadata the metadata of the action
+ */
void onAction(int id, String metadata);
}
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 70dd10f2c371..5fa8125ced10 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -722,7 +722,7 @@ public interface AndroidPackage {
* The names of packages to adopt ownership of permissions from, parsed under {@link
* ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
*
- * @see R.styleable#AndroidManifestOriginalPackage_name
+ * @see R.styleable#AndroidManifestAdoptPermissions_name
* @hide
*/
@NonNull
diff --git a/core/java/org/chromium/arc/EventLogTags.logtags b/core/java/org/chromium/arc/EventLogTags.logtags
index 1b7160e90224..8102d6f10ed4 100644
--- a/core/java/org/chromium/arc/EventLogTags.logtags
+++ b/core/java/org/chromium/arc/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package org.chromium.arc
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b7a7f96e4e1c..027113a75f6b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -220,6 +220,7 @@ cc_library_shared_for_libandroid_runtime {
"android_hardware_camera2_utils_SurfaceUtils.cpp",
"android_hardware_display_DisplayManagerGlobal.cpp",
"android_hardware_display_DisplayViewport.cpp",
+ "android_hardware_display_DisplayTopology.cpp",
"android_hardware_HardwareBuffer.cpp",
"android_hardware_OverlayProperties.cpp",
"android_hardware_SensorManager.cpp",
@@ -262,6 +263,7 @@ cc_library_shared_for_libandroid_runtime {
"com_android_internal_os_ZygoteCommandBuffer.cpp",
"com_android_internal_os_ZygoteInit.cpp",
"com_android_internal_security_VerityUtils.cpp",
+ "com_android_internal_util_ArrayUtils.cpp",
"hwbinder/EphemeralStorage.cpp",
"fd_utils.cpp",
"android_hardware_input_InputWindowHandle.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ac187b08f0f1..78d69f0714e0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -220,6 +220,7 @@ extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
+extern int register_com_android_internal_util_ArrayUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_android_window_WindowInfosListener(JNIEnv* env);
extern int register_android_window_ScreenCapture(JNIEnv* env);
@@ -1621,6 +1622,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_com_android_internal_security_VerityUtils),
+ REG_JNI(register_com_android_internal_util_ArrayUtils),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
index ead66660a0a4..12585d5f8137 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.cpp
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -28,24 +28,77 @@
#include "core_jni_helpers.h"
#include "android_app_PropertyInvalidatedCache.h"
+namespace android::app::PropertyInvalidatedCache {
+
+// These provide run-time access to the sizing parameters.
+int NonceStore::getMaxNonce() const {
+ return kMaxNonce;
+}
+
+size_t NonceStore::getMaxByte() const {
+ return kMaxByte;
+}
+
+// Fetch a nonce, returning UNSET if the index is out of range. This method specifically
+// does not throw or generate an error if the index is out of range; this allows the method
+// to be called in a CriticalNative JNI API.
+int64_t NonceStore::getNonce(int index) const {
+ if (index < 0 || index >= kMaxNonce) {
+ return UNSET;
+ } else {
+ return nonce()[index];
+ }
+}
+
+// Set a nonce and return true. Return false if the index is out of range. This method
+// specifically does not throw or generate an error if the index is out of range; this
+// allows the method to be called in a CriticalNative JNI API.
+bool NonceStore::setNonce(int index, int64_t value) {
+ if (index < 0 || index >= kMaxNonce) {
+ return false;
+ } else {
+ nonce()[index] = value;
+ return true;
+ }
+}
+
+// Fetch just the byte-block hash
+int32_t NonceStore::getHash() const {
+ return mByteHash;
+}
+
+// Copy the byte block to the target and return the current hash.
+int32_t NonceStore::getByteBlock(block_t* block, size_t len) const {
+ memcpy(block, (void*) byteBlock(), std::min(kMaxByte, len));
+ return mByteHash;
+}
+
+// Set the byte block and the hash.
+void NonceStore::setByteBlock(int hash, const block_t* block, size_t len) {
+ memcpy((void*) byteBlock(), block, len = std::min(kMaxByte, len));
+ mByteHash = hash;
+}
+
+} // namespace android::app::PropertyInvalidatedCache;
+
namespace {
using namespace android::app::PropertyInvalidatedCache;
// Convert a jlong to a nonce block. This is a convenience function that should be inlined by
// the compiler.
-inline SystemCacheNonce* sysCache(jlong ptr) {
- return reinterpret_cast<SystemCacheNonce*>(ptr);
+inline NonceStore* nonceCache(jlong ptr) {
+ return reinterpret_cast<NonceStore*>(ptr);
}
// Return the number of nonces in the nonce block.
jint getMaxNonce(JNIEnv*, jclass, jlong ptr) {
- return sysCache(ptr)->getMaxNonce();
+ return nonceCache(ptr)->getMaxNonce();
}
// Return the number of string bytes in the nonce block.
jint getMaxByte(JNIEnv*, jclass, jlong ptr) {
- return sysCache(ptr)->getMaxByte();
+ return nonceCache(ptr)->getMaxByte();
}
// Set the byte block. The first int is the hash to set and the second is the array to copy.
@@ -56,25 +109,25 @@ void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block");
return;
}
- sysCache(ptr)->setByteBlock(hash, value.get(), value.size());
+ nonceCache(ptr)->setByteBlock(hash, value.get(), value.size());
}
// Fetch the byte block. If the incoming hash is the same as the local hash, the Java layer is
// presumed to have an up-to-date copy of the byte block; do not copy byte array. The local
// hash is returned.
jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
- if (sysCache(ptr)->getHash() == hash) {
+ if (nonceCache(ptr)->getHash() == hash) {
return hash;
}
ScopedByteArrayRW value(env, val);
- return sysCache(ptr)->getByteBlock(value.get(), value.size());
+ return nonceCache(ptr)->getByteBlock(value.get(), value.size());
}
// Fetch the byte block hash.
//
// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters.
jint getByteBlockHash(jlong ptr) {
- return sysCache(ptr)->getHash();
+ return nonceCache(ptr)->getHash();
}
// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is
@@ -83,7 +136,7 @@ jint getByteBlockHash(jlong ptr) {
//
// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
jlong getNonce(jlong ptr, jint index) {
- return sysCache(ptr)->getNonce(index);
+ return nonceCache(ptr)->getNonce(index);
}
// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if
@@ -92,7 +145,7 @@ jlong getNonce(jlong ptr, jint index) {
//
// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
jboolean setNonce(jlong ptr, jint index, jlong value) {
- return sysCache(ptr)->setNonce(index, value);
+ return nonceCache(ptr)->setNonce(index, value);
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
index eefa8fa88624..00aa281b572f 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.h
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -18,129 +18,139 @@
#include <memory.h>
#include <atomic>
+#include <cstdint>
-namespace android {
-namespace app {
-namespace PropertyInvalidatedCache {
+namespace android::app::PropertyInvalidatedCache {
/**
- * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
- * byte array has an associated hash. This class provides methods to read and write the fields
- * of the block but it does not interpret the fields.
- *
- * On initialization, all fields are set to zero.
- *
- * In general, methods do not report errors. This allows the methods to be used in
- * CriticalNative JNI APIs.
- *
- * The template is parameterized by the number of nonces it supports and the number of bytes in
- * the string block.
+ * A head of a CacheNonce object. This contains all the fields that have a fixed size and
+ * location. Fields with a variable location are found via offsets. The offsets make this
+ * object position-independent, which is required because it is in shared memory and would be
+ * mapped into different virtual addresses for different processes.
*/
-template<int maxNonce, size_t maxByte> class CacheNonce {
-
- // The value of an unset field.
- static const int UNSET = 0;
-
+class NonceStore {
+ protected:
// A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
// signed char.
typedef signed char block_t;
- // The array of nonces
- volatile std::atomic<int64_t> mNonce[maxNonce];
+ // The nonce type.
+ typedef std::atomic<int64_t> nonce_t;
- // The byte array. This is not atomic but it is guarded by the mByteHash.
- volatile block_t mByteBlock[maxByte];
+ // Atomics should be safe to use across processes if they are lock free.
+ static_assert(nonce_t::is_always_lock_free == true);
- // The hash that validates the byte block
- volatile std::atomic<int32_t> mByteHash;
+ // The value of an unset field.
+ static constexpr int UNSET = 0;
- // Pad the class to a multiple of 8 bytes.
- int32_t _pad;
+ // The size of the nonce array.
+ const int32_t kMaxNonce;
- public:
+ // The size of the byte array.
+ const size_t kMaxByte;
- // The expected size of this instance. This is a compile-time constant and can be used in a
- // static assertion.
- static const int expectedSize =
- maxNonce * sizeof(std::atomic<int64_t>)
- + sizeof(std::atomic<int32_t>)
- + maxByte * sizeof(block_t)
- + sizeof(int32_t);
+ // The offset to the nonce array.
+ const size_t mNonceOffset;
- // These provide run-time access to the sizing parameters.
- int getMaxNonce() const {
- return maxNonce;
- }
+ // The offset to the byte array.
+ const size_t mByteOffset;
- size_t getMaxByte() const {
- return maxByte;
- }
+ // The byte block hash. This is fixed and at a known offset, so leave it in the base class.
+ volatile std::atomic<int32_t> mByteHash;
- // Construct and initialize the memory.
- CacheNonce() {
- for (int i = 0; i < maxNonce; i++) {
- mNonce[i] = UNSET;
- }
- mByteHash = UNSET;
- memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ // The constructor is protected! It only makes sense when called from a subclass.
+ NonceStore(int kMaxNonce, size_t kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
+ kMaxNonce(kMaxNonce),
+ kMaxByte(kMaxByte),
+ mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))),
+ mByteOffset(offset(this, const_cast<block_t*>(block))) {
}
+ public:
+
+ // These provide run-time access to the sizing parameters.
+ int getMaxNonce() const;
+ size_t getMaxByte() const;
+
// Fetch a nonce, returning UNSET if the index is out of range. This method specifically
// does not throw or generate an error if the index is out of range; this allows the method
// to be called in a CriticalNative JNI API.
- int64_t getNonce(int index) const {
- if (index < 0 || index >= maxNonce) {
- return UNSET;
- } else {
- return mNonce[index];
- }
- }
+ int64_t getNonce(int index) const;
// Set a nonce and return true. Return false if the index is out of range. This method
// specifically does not throw or generate an error if the index is out of range; this
// allows the method to be called in a CriticalNative JNI API.
- bool setNonce(int index, int64_t value) {
- if (index < 0 || index >= maxNonce) {
- return false;
- } else {
- mNonce[index] = value;
- return true;
- }
- }
+ bool setNonce(int index, int64_t value);
// Fetch just the byte-block hash
- int32_t getHash() const {
- return mByteHash;
- }
+ int32_t getHash() const;
// Copy the byte block to the target and return the current hash.
- int32_t getByteBlock(block_t* block, size_t len) const {
- memcpy(block, (void*) mByteBlock, std::min(maxByte, len));
- return mByteHash;
- }
+ int32_t getByteBlock(block_t* block, size_t len) const;
// Set the byte block and the hash.
- void setByteBlock(int hash, const block_t* block, size_t len) {
- memcpy((void*) mByteBlock, block, len = std::min(maxByte, len));
- mByteHash = hash;
+ void setByteBlock(int hash, const block_t* block, size_t len);
+
+ private:
+
+ // A convenience function to compute the offset between two unlike pointers.
+ static size_t offset(void const* base, void const* member) {
+ return reinterpret_cast<uintptr_t>(member) - reinterpret_cast<std::uintptr_t>(base);
+ }
+
+ // Return the address of the nonce array.
+ volatile nonce_t* nonce() const {
+ // The array is located at an offset from <this>.
+ return reinterpret_cast<nonce_t*>(
+ reinterpret_cast<std::uintptr_t>(this) + mNonceOffset);
+ }
+
+ // Return the address of the byte block array.
+ volatile block_t* byteBlock() const {
+ // The array is located at an offset from <this>.
+ return reinterpret_cast<block_t*>(
+ reinterpret_cast<std::uintptr_t>(this) + mByteOffset);
}
};
/**
- * Sizing parameters for the system_server PropertyInvalidatedCache support. A client can
- * retrieve the values through the accessors in CacheNonce instances.
+ * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
+ * byte array has an associated hash. This class provides methods to read and write the fields
+ * of the block but it does not interpret the fields.
+ *
+ * On initialization, all fields are set to zero.
+ *
+ * In general, methods do not report errors. This allows the methods to be used in
+ * CriticalNative JNI APIs.
+ *
+ * The template is parameterized by the number of nonces it supports and the number of bytes in
+ * the string block.
*/
-static const int MAX_NONCE = 64;
-static const int BYTE_BLOCK_SIZE = 8192;
+template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore {
+
+ // The array of nonces
+ volatile nonce_t mNonce[maxNonce];
-// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.
-typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce;
+ // The byte array. This is not atomic but it is guarded by the mByteHash.
+ volatile block_t mByteBlock[maxByte];
+
+ public:
+ // Construct and initialize the memory.
+ CacheNonce() :
+ NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0])
+ {
+ for (int i = 0; i < maxNonce; i++) {
+ mNonce[i] = UNSET;
+ }
+ mByteHash = UNSET;
+ memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ }
+};
-// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
-// and 64-bit systems.
-static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize,
- "Unexpected SystemCacheNonce size");
+// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes. This is
+// more than enough for system_server PropertyInvalidatedCache support. The configuration
+// values are not defined as visible constants. Clients should use the accessors on the
+// SystemCacheNonce instance if they need the sizing parameters.
+typedef CacheNonce</* max nonce */ 64, /* byte block size */ 8192> SystemCacheNonce;
-} // namespace PropertyInvalidatedCache
-} // namespace app
-} // namespace android
+} // namespace android.app.PropertyInvalidatedCache
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 85a6bdf95928..5fa808361178 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -70,12 +70,33 @@ static void throwInvalidParameter(JNIEnv *env, jlong stmtPtr, jint index) {
}
}
+// If the last operation failed, throw an exception and return true. Otherwise return false.
+static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
+ switch (sqlite3_errcode(db(stmtPtr))) {
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ case SQLITE_ROW: return false;
+ }
+ throw_sqlite3_exception(env, db(stmtPtr), nullptr);
+ return true;
+}
-// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
-// of bounds. It returns true if an exception was thrown.
+// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
+// bounds of the row data set. It throws SQLiteMisuseException if the statement's data column
+// count is zero; that generally occurs because the client has forgotten to call step() or the
+// client has stepped past the end of the query. The function returns true if an exception was
+// thrown.
static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
- if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
- int count = sqlite3_data_count(stmt(stmtPtr));
+ int count = sqlite3_data_count(stmt(stmtPtr));
+ if (throwIfError(env, stmtPtr)) {
+ return true;
+ } else if (count == 0) {
+ // A count of zero indicates a misuse: the statement has never been step()'ed.
+ const char* message = "row has no data";
+ const char* errmsg = sqlite3_errstr(SQLITE_MISUSE);
+ throw_sqlite3_exception(env, SQLITE_MISUSE, errmsg, message);
+ return true;
+ } else if (col < 0 || col >= count) {
std::string message = android::base::StringPrintf(
"column index %d out of bounds [0,%d]", col, count - 1);
char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
@@ -86,15 +107,22 @@ static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
}
}
-// If the last operation failed, throw an exception and return true. Otherwise return false.
-static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
- switch (sqlite3_errcode(db(stmtPtr))) {
- case SQLITE_OK:
- case SQLITE_DONE:
- case SQLITE_ROW: return false;
+// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
+// bounds of the result set. (This is not the same as the data columns in a row). The function
+// returns true if an exception was thrown.
+static bool throwIfInvalidResultColumn(JNIEnv *env, jlong stmtPtr, jint col) {
+ int count = sqlite3_column_count(stmt(stmtPtr));
+ if (throwIfError(env, stmtPtr)) {
+ return true;
+ } else if (col < 0 || col >= count) {
+ std::string message = android::base::StringPrintf(
+ "column index %d out of bounds [0,%d]", col, count - 1);
+ char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
+ throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
+ return true;
+ } else {
+ return false;
}
- throw_sqlite3_exception(env, db(stmtPtr), nullptr);
- return true;
}
static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
@@ -226,7 +254,7 @@ static jint columnType(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
}
static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ if (throwIfInvalidResultColumn(env, stmtPtr, col)) {
return nullptr;
}
const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b9c3bf73f11c..7b61a5db0b41 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -87,6 +87,38 @@ private:
jobject mTransactionHangObject;
};
+struct {
+ jmethodID onWaitForBufferRelease;
+} gWaitForBufferReleaseCallback;
+
+class WaitForBufferReleaseCallbackWrapper
+ : public LightRefBase<WaitForBufferReleaseCallbackWrapper> {
+public:
+ explicit WaitForBufferReleaseCallbackWrapper(JNIEnv* env, jobject jobject) {
+ env->GetJavaVM(&mVm);
+ mWaitForBufferReleaseObject = env->NewGlobalRef(jobject);
+ LOG_ALWAYS_FATAL_IF(!mWaitForBufferReleaseObject, "Failed to make global ref");
+ }
+
+ ~WaitForBufferReleaseCallbackWrapper() {
+ if (mWaitForBufferReleaseObject != nullptr) {
+ getenv(mVm)->DeleteGlobalRef(mWaitForBufferReleaseObject);
+ mWaitForBufferReleaseObject = nullptr;
+ }
+ }
+
+ void onWaitForBufferRelease() {
+ JNIEnv* env = getenv(mVm);
+ getenv(mVm)->CallVoidMethod(mWaitForBufferReleaseObject,
+ gWaitForBufferReleaseCallback.onWaitForBufferRelease);
+ DieIfException(env, "Uncaught exception in WaitForBufferReleaseCallback.");
+ }
+
+private:
+ JavaVM* mVm;
+ jobject mWaitForBufferReleaseObject;
+};
+
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
jboolean updateDestinationFrame) {
ScopedUtfChars name(env, jName);
@@ -215,6 +247,18 @@ static void nativeSetApplyToken(JNIEnv* env, jclass clazz, jlong ptr, jobject ap
return queue->setApplyToken(std::move(token));
}
+static void nativeSetWaitForBufferReleaseCallback(JNIEnv* env, jclass clazz, jlong ptr,
+ jobject waitForBufferReleaseCallback) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ if (waitForBufferReleaseCallback == nullptr) {
+ queue->setWaitForBufferReleaseCallback(nullptr);
+ } else {
+ sp<WaitForBufferReleaseCallbackWrapper> wrapper =
+ new WaitForBufferReleaseCallbackWrapper{env, waitForBufferReleaseCallback};
+ queue->setWaitForBufferReleaseCallback([wrapper]() { wrapper->onWaitForBufferRelease(); });
+ }
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
@@ -234,6 +278,9 @@ static const JNINativeMethod gMethods[] = {
"(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
(void*)nativeSetTransactionHangCallback},
{"nativeSetApplyToken", "(JLandroid/os/IBinder;)V", (void*)nativeSetApplyToken},
+ {"nativeSetWaitForBufferReleaseCallback",
+ "(JLandroid/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback;)V",
+ (void*)nativeSetWaitForBufferReleaseCallback},
// clang-format on
};
@@ -255,6 +302,10 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
gTransactionHangCallback.onTransactionHang =
GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang",
"(Ljava/lang/String;)V");
+ jclass waitForBufferReleaseClass =
+ FindClassOrDie(env, "android/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback");
+ gWaitForBufferReleaseCallback.onWaitForBufferRelease =
+ GetMethodIDOrDie(env, waitForBufferReleaseClass, "onWaitForBufferRelease", "()V");
return 0;
}
diff --git a/core/jni/android_hardware_display_DisplayTopology.cpp b/core/jni/android_hardware_display_DisplayTopology.cpp
new file mode 100644
index 000000000000..d9e802de81e0
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayTopology.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "DisplayTopology-JNI"
+
+#include <android_hardware_display_DisplayTopology.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <utils/Errors.h>
+
+#include "jni_wrappers.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+ jfieldID primaryDisplayId;
+ jfieldID displayNodes;
+} gDisplayTopologyGraphClassInfo;
+
+static struct {
+ jclass clazz;
+ jfieldID displayId;
+ jfieldID adjacentDisplays;
+} gDisplayTopologyGraphNodeClassInfo;
+
+static struct {
+ jclass clazz;
+ jfieldID displayId;
+ jfieldID position;
+ jfieldID offsetPx;
+} gDisplayTopologyGraphAdjacentDisplayClassInfo;
+
+// ----------------------------------------------------------------------------
+
+status_t android_hardware_display_DisplayTopologyAdjacentDisplay_toNative(
+ JNIEnv* env, jobject adjacentDisplayObj, DisplayTopologyAdjacentDisplay* adjacentDisplay) {
+ adjacentDisplay->displayId = ui::LogicalDisplayId{
+ env->GetIntField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.displayId)};
+ adjacentDisplay->position = static_cast<DisplayTopologyPosition>(
+ env->GetIntField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.position));
+ adjacentDisplay->offsetPx =
+ env->GetFloatField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx);
+ return OK;
+}
+
+status_t android_hardware_display_DisplayTopologyGraphNode_toNative(
+ JNIEnv* env, jobject nodeObj,
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&
+ graph) {
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId{
+ env->GetIntField(nodeObj, gDisplayTopologyGraphNodeClassInfo.displayId)};
+
+ jobjectArray adjacentDisplaysArray = static_cast<jobjectArray>(
+ env->GetObjectField(nodeObj, gDisplayTopologyGraphNodeClassInfo.adjacentDisplays));
+
+ if (adjacentDisplaysArray) {
+ jsize length = env->GetArrayLength(adjacentDisplaysArray);
+ for (jsize i = 0; i < length; i++) {
+ ScopedLocalRef<jobject>
+ adjacentDisplayObj(env, env->GetObjectArrayElement(adjacentDisplaysArray, i));
+ if (NULL != adjacentDisplayObj.get()) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ DisplayTopologyAdjacentDisplay adjacentDisplay;
+ android_hardware_display_DisplayTopologyAdjacentDisplay_toNative(env,
+ adjacentDisplayObj
+ .get(),
+ &adjacentDisplay);
+ graph[displayId].push_back(adjacentDisplay);
+ }
+ }
+ return OK;
+}
+
+DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(JNIEnv* env,
+ jobject topologyObj) {
+ DisplayTopologyGraph topology;
+ topology.primaryDisplayId = ui::LogicalDisplayId{
+ env->GetIntField(topologyObj, gDisplayTopologyGraphClassInfo.primaryDisplayId)};
+
+ jobjectArray nodesArray = static_cast<jobjectArray>(
+ env->GetObjectField(topologyObj, gDisplayTopologyGraphClassInfo.displayNodes));
+
+ if (nodesArray) {
+ jsize length = env->GetArrayLength(nodesArray);
+ for (jsize i = 0; i < length; i++) {
+ ScopedLocalRef<jobject> nodeObj(env, env->GetObjectArrayElement(nodesArray, i));
+ if (NULL != nodeObj.get()) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ android_hardware_display_DisplayTopologyGraphNode_toNative(env, nodeObj.get(),
+ topology.graph);
+ }
+ }
+ return topology;
+}
+
+// ----------------------------------------------------------------------------
+
+int register_android_hardware_display_DisplayTopology(JNIEnv* env) {
+ jclass graphClazz = FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph");
+ gDisplayTopologyGraphClassInfo.clazz = MakeGlobalRefOrDie(env, graphClazz);
+
+ gDisplayTopologyGraphClassInfo.primaryDisplayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphClassInfo.clazz, "primaryDisplayId", "I");
+ gDisplayTopologyGraphClassInfo.displayNodes =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphClassInfo.clazz, "displayNodes",
+ "[Landroid/hardware/display/DisplayTopologyGraph$DisplayNode;");
+
+ jclass displayNodeClazz =
+ FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph$DisplayNode");
+ gDisplayTopologyGraphNodeClassInfo.clazz = MakeGlobalRefOrDie(env, displayNodeClazz);
+ gDisplayTopologyGraphNodeClassInfo.displayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "displayId", "I");
+ gDisplayTopologyGraphNodeClassInfo.adjacentDisplays =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "adjacentDisplays",
+ "[Landroid/hardware/display/DisplayTopologyGraph$AdjacentDisplay;");
+
+ jclass adjacentDisplayClazz =
+ FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph$AdjacentDisplay");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz =
+ MakeGlobalRefOrDie(env, adjacentDisplayClazz);
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.displayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "displayId",
+ "I");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.position =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "position",
+ "I");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "offsetPx",
+ "F");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_hardware_display_DisplayTopology.h b/core/jni/android_hardware_display_DisplayTopology.h
new file mode 100644
index 000000000000..390191f827d8
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayTopology.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <input/DisplayTopologyGraph.h>
+
+#include "jni.h"
+
+namespace android {
+
+/**
+ * Copies the contents of a DVM DisplayTopology object to a new native DisplayTopology instance.
+ * Returns DisplayTopology.
+ */
+extern DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(
+ JNIEnv* env, jobject eventObj);
+
+} // namespace android
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 704aef3cd131..4ba1ae9d670d 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -51,6 +51,18 @@
#define ENCODING_DTS_UHD_P2 30
#define ENCODING_DSD 31
#define ENCODING_AC4_L4 32
+#define ENCODING_IAMF_SIMPLE_PROFILE_OPUS 33
+#define ENCODING_IAMF_SIMPLE_PROFILE_AAC 34
+#define ENCODING_IAMF_SIMPLE_PROFILE_FLAC 35
+#define ENCODING_IAMF_SIMPLE_PROFILE_PCM 36
+#define ENCODING_IAMF_BASE_PROFILE_OPUS 37
+#define ENCODING_IAMF_BASE_PROFILE_AAC 38
+#define ENCODING_IAMF_BASE_PROFILE_FLAC 39
+#define ENCODING_IAMF_BASE_PROFILE_PCM 40
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS 41
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC 42
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC 43
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM 44
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -128,6 +140,30 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_DTS_UHD_P2;
case ENCODING_DSD:
return AUDIO_FORMAT_DSD;
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_SIMPLE_OPUS;
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_SIMPLE_AAC;
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_SIMPLE_FLAC;
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_SIMPLE_PCM;
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_BASE_OPUS;
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_BASE_AAC;
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_BASE_FLAC;
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_BASE_PCM;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -211,6 +247,30 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
return ENCODING_DEFAULT;
case AUDIO_FORMAT_DSD:
return ENCODING_DSD;
+ case AUDIO_FORMAT_IAMF_SIMPLE_OPUS:
+ return ENCODING_IAMF_SIMPLE_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_SIMPLE_AAC:
+ return ENCODING_IAMF_SIMPLE_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_SIMPLE_FLAC:
+ return ENCODING_IAMF_SIMPLE_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_SIMPLE_PCM:
+ return ENCODING_IAMF_SIMPLE_PROFILE_PCM;
+ case AUDIO_FORMAT_IAMF_BASE_OPUS:
+ return ENCODING_IAMF_BASE_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_BASE_AAC:
+ return ENCODING_IAMF_BASE_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_BASE_FLAC:
+ return ENCODING_IAMF_BASE_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_BASE_PCM:
+ return ENCODING_IAMF_BASE_PROFILE_PCM;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM;
default:
return ENCODING_INVALID;
}
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 805d5ad41e83..cd39e6f93fb4 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -34,23 +34,27 @@
namespace android {
namespace {
-std::atomic<selabel_handle*> sehandle{nullptr};
+std::atomic<selabel_handle *> file_sehandle{nullptr};
-selabel_handle* GetSELabelHandle() {
- selabel_handle* h = sehandle.load();
+selabel_handle *GetSELabelHandle_impl(selabel_handle *(*handle_func)(),
+ std::atomic<selabel_handle *> *handle_cache) {
+ selabel_handle *h = handle_cache->load();
if (h != nullptr) {
return h;
}
- h = selinux_android_file_context_handle();
+ h = handle_func();
selabel_handle* expected = nullptr;
- if (!sehandle.compare_exchange_strong(expected, h)) {
+ if (!handle_cache->compare_exchange_strong(expected, h)) {
selabel_close(h);
- return sehandle.load();
+ return handle_cache->load();
}
return h;
}
+selabel_handle *GetSELabelFileBackendHandle() {
+ return GetSELabelHandle_impl(selinux_android_file_context_handle, &file_sehandle);
+}
}
struct SecurityContext_Delete {
@@ -106,7 +110,7 @@ static jstring fileSelabelLookup(JNIEnv* env, jobject, jstring pathStr) {
return NULL;
}
- auto* selabel_handle = GetSELabelHandle();
+ auto *selabel_handle = GetSELabelFileBackendHandle();
if (selabel_handle == NULL) {
ALOGE("fileSelabelLookup => Failed to get SEHandle");
return NULL;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 7ef7829c6ba5..dc7253954d44 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -589,32 +589,6 @@ jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz,
return pri;
}
-jboolean android_os_Process_setSwappiness(JNIEnv *env, jobject clazz,
- jint pid, jboolean is_increased)
-{
- char text[64];
-
- if (is_increased) {
- strcpy(text, "/sys/fs/cgroup/memory/sw/tasks");
- } else {
- strcpy(text, "/sys/fs/cgroup/memory/tasks");
- }
-
- struct stat st;
- if (stat(text, &st) || !S_ISREG(st.st_mode)) {
- return false;
- }
-
- int fd = open(text, O_WRONLY | O_CLOEXEC);
- if (fd >= 0) {
- sprintf(text, "%" PRId32, pid);
- write(fd, text, strlen(text));
- close(fd);
- }
-
- return true;
-}
-
void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
{
if (name == NULL) {
@@ -1396,7 +1370,6 @@ static const JNINativeMethod methods[] = {
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
{"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup},
{"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
- {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
{"setArgV0Native", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},
{"setGid", "(I)I", (void*)android_os_Process_setGid},
diff --git a/core/jni/android_view_SurfaceControlActivePictureListener.cpp b/core/jni/android_view_SurfaceControlActivePictureListener.cpp
index 91849c1514cc..15132db2a569 100644
--- a/core/jni/android_view_SurfaceControlActivePictureListener.cpp
+++ b/core/jni/android_view_SurfaceControlActivePictureListener.cpp
@@ -106,12 +106,11 @@ struct SurfaceControlActivePictureListener : public gui::BnActivePictureListener
}
status_t startListening() {
- // TODO(b/337330263): Make SF multiple-listener capable
- return SurfaceComposerClient::setActivePictureListener(this);
+ return SurfaceComposerClient::addActivePictureListener(this);
}
status_t stopListening() {
- return SurfaceComposerClient::setActivePictureListener(nullptr);
+ return SurfaceComposerClient::removeActivePictureListener(this);
}
protected:
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 1a52fb74a1e9..5657fa146b5b 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -110,13 +110,19 @@ public:
captureResults.fenceResult.value()->waitForever(LOG_TAG);
jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
env, captureResults.buffer->toAHardwareBuffer());
+ jobject jGainmap = nullptr;
+ if (captureResults.optionalGainMap) {
+ jGainmap = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.optionalGainMap->toAHardwareBuffer());
+ }
jobject screenshotHardwareBuffer =
env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
gScreenshotHardwareBufferClassInfo.builder,
jhardwareBuffer,
static_cast<jint>(captureResults.capturedDataspace),
captureResults.capturedSecureLayers,
- captureResults.capturedHdrLayers);
+ captureResults.capturedHdrLayers, jGainmap,
+ captureResults.hdrSdrRatio);
checkAndClearException(env, "builder");
env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer,
fenceStatus(captureResults.fenceResult));
@@ -340,7 +346,8 @@ int register_android_window_ScreenCapture(JNIEnv* env) {
MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
gScreenshotHardwareBufferClassInfo.builder =
GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
- "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/window/"
+ "(Landroid/hardware/HardwareBuffer;IZZLandroid/hardware/"
+ "HardwareBuffer;F)Landroid/window/"
"ScreenCapture$ScreenshotHardwareBuffer;");
return err;
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 76ead2a3ca31..48c92c87f54e 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -21,8 +21,6 @@
#include <android-base/file.h>
#include <android-base/hex.h>
#include <android-base/unique_fd.h>
-#include <bionic/macros.h>
-#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/fs.h>
@@ -48,8 +46,8 @@ bool punchWithBlockAlignment(borrowed_fd fd, uint64_t start, uint64_t length, ui
return false;
}
- start = align_up(start, blockSize);
- end = align_down(end, blockSize);
+ start = __builtin_align_up(start, blockSize);
+ end = __builtin_align_down(end, blockSize);
uint64_t alignedLength;
if (__builtin_sub_overflow(end, start, &alignedLength)) {
@@ -67,7 +65,7 @@ bool punchWithBlockAlignment(borrowed_fd fd, uint64_t start, uint64_t length, ui
int result =
fallocate(fd.get(), FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, start, alignedLength);
if (result < 0) {
- ALOGE("fallocate failed to punch hole, error:%d", errno);
+ ALOGE("fallocate failed to punch hole: %m");
return false;
}
@@ -78,7 +76,7 @@ bool punchHoles(const char *filePath, const uint64_t offset,
const std::vector<Elf64_Phdr> &programHeaders) {
struct stat64 beforePunch;
if (int result = lstat64(filePath, &beforePunch); result != 0) {
- ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ ALOGE("lstat64 failed for filePath %s: %m", filePath);
return false;
}
@@ -190,7 +188,7 @@ bool punchHoles(const char *filePath, const uint64_t offset,
IF_ALOGD() {
struct stat64 afterPunch;
if (int result = lstat64(filePath, &afterPunch); result != 0) {
- ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ ALOGD("lstat64 failed for filePath %s: %m", filePath);
return false;
}
ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
@@ -269,7 +267,7 @@ bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldL
struct stat64 beforePunch;
if (int result = lstat64(filePath, &beforePunch); result != 0) {
- ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ ALOGE("lstat64 failed for filePath %s: %m", filePath);
return false;
}
@@ -348,7 +346,7 @@ bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldL
IF_ALOGD() {
struct stat64 afterPunch;
if (int result = lstat64(filePath, &afterPunch); result != 0) {
- ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ ALOGD("lstat64 failed for filePath %s: %m", filePath);
return false;
}
ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index b2eeff36c007..f40cfd9f8e51 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -532,7 +532,12 @@ static inline bool app_compat_16kb_enabled() {
static const size_t kPageSize = getpagesize();
// App compat is only applicable on 16kb-page-size devices.
- return kPageSize == 0x4000;
+ if (kPageSize != 0x4000) {
+ return false;
+ }
+
+ // Explicit disabled status for app compat
+ return !android::base::GetBoolProperty("pm.16kb.app_compat.disabled", false);
}
static jint
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aeaeeca3e845..8c7b335e6e86 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -88,8 +88,8 @@
#include "nativebridge/native_bridge.h"
#if defined(__BIONIC__)
+#include <android/dlext_private.h>
extern "C" void android_reset_stack_guards();
-extern "C" void android_set_16kb_appcompat_mode(bool enable_app_compat);
#endif
namespace {
diff --git a/core/jni/com_android_internal_util_ArrayUtils.cpp b/core/jni/com_android_internal_util_ArrayUtils.cpp
new file mode 100644
index 000000000000..c70625815b90
--- /dev/null
+++ b/core/jni/com_android_internal_util_ArrayUtils.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ArrayUtils"
+
+#include <android-base/logging.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <string.h>
+#include <unistd.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static size_t GetCacheLineSize() {
+ long size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+ if (size <= 0) {
+ ALOGE("Unable to determine L1 data cache line size. Assuming 32 bytes");
+ return 32;
+ }
+ // The cache line size should always be a power of 2.
+ CHECK((size & (size - 1)) == 0);
+
+ return size;
+}
+
+static void CleanCacheLineContainingAddress(const uint8_t* p) {
+#if defined(__aarch64__)
+ // 'dc cvac' stands for "Data Cache line Clean by Virtual Address to point-of-Coherency".
+ // It writes the cache line back to the "point-of-coherency", i.e. main memory.
+ asm volatile("dc cvac, %0" ::"r"(p));
+#elif defined(__i386__) || defined(__x86_64__)
+ asm volatile("clflush (%0)" ::"r"(p));
+#elif defined(__riscv)
+ // This should eventually work, but it is not ready to be enabled yet:
+ // 1.) The Android emulator needs to add support for zicbom.
+ // 2.) Kernel needs to enable zicbom in usermode.
+ // 3.) Android clang needs to add zicbom to the target.
+ // asm volatile("cbo.clean (%0)" ::"r"(p));
+#elif defined(__arm__)
+ // arm32 has a cacheflush() syscall, but it is undocumented and only flushes the icache.
+ // It is not the same as cacheflush(2) as documented in the Linux man-pages project.
+#else
+#error "Unknown architecture"
+#endif
+}
+
+static void CleanDataCache(const uint8_t* p, size_t buffer_size, size_t cache_line_size) {
+ // Clean the first line that overlaps the buffer.
+ CleanCacheLineContainingAddress(p);
+ // Clean any additional lines that overlap the buffer. Use cache-line-aligned addresses to
+ // ensure that (a) the last cache line gets flushed, and (b) no cache line is flushed twice.
+ for (size_t i = cache_line_size - ((uintptr_t)p & (cache_line_size - 1)); i < buffer_size;
+ i += cache_line_size) {
+ CleanCacheLineContainingAddress(p + i);
+ }
+}
+
+static void ZeroizePrimitiveArray(JNIEnv* env, jclass clazz, jarray array, size_t component_len) {
+ static const size_t cache_line_size = GetCacheLineSize();
+
+ if (array == nullptr) {
+ return;
+ }
+
+ size_t buffer_size = env->GetArrayLength(array) * component_len;
+ if (buffer_size == 0) {
+ return;
+ }
+
+ // ART guarantees that GetPrimitiveArrayCritical never copies.
+ jboolean isCopy;
+ void* elems = env->GetPrimitiveArrayCritical(array, &isCopy);
+ CHECK(!isCopy);
+
+#ifdef __BIONIC__
+ memset_explicit(elems, 0, buffer_size);
+#else
+ memset(elems, 0, buffer_size);
+#endif
+ // Clean the data cache so that the data gets zeroized in main memory right away. Without this,
+ // it might not be written to main memory until the cache line happens to be evicted.
+ CleanDataCache(static_cast<const uint8_t*>(elems), buffer_size, cache_line_size);
+
+ env->ReleasePrimitiveArrayCritical(array, elems, /* mode= */ 0);
+}
+
+static void ZeroizeByteArray(JNIEnv* env, jclass clazz, jbyteArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jbyte));
+}
+
+static void ZeroizeCharArray(JNIEnv* env, jclass clazz, jcharArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jchar));
+}
+
+static const JNINativeMethod sMethods[] = {
+ {"zeroize", "([B)V", (void*)ZeroizeByteArray},
+ {"zeroize", "([C)V", (void*)ZeroizeCharArray},
+};
+
+int register_com_android_internal_util_ArrayUtils(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/internal/util/ArrayUtils", sMethods,
+ NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 2e0fe9eb13d9..7e9d62315ef1 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -105,6 +105,7 @@ message SecureSettingsProto {
optional SettingProto accessibility_gesture_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto display_daltonizer_saturation_level = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto hct_rect_prompt_status = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 903d08b9a2ab..be4fb8bdecfb 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -179,6 +179,7 @@ android_app {
"art-aconfig-flags",
"ranging_aconfig_flags",
"aconfig_settingslib_flags",
+ "telephony_flags",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a86d55125887..4a948dd91fb0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4149,6 +4149,14 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to manage policy related to AppFunctions.
+ <p>Protection level: internal|role
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS"
+ android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to set policy related to subscriptions downloaded by an admin.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
@@ -4527,11 +4535,11 @@
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES"
android:protectionLevel="normal" />
- <!-- Allows application to request to be associated with a virtual display capable of streaming
+ <!-- Allows application to request to be associated with a virtual device capable of streaming
Android applications
({@link android.companion.AssociationRequest#DEVICE_PROFILE_APP_STREAMING})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
android:protectionLevel="signature|privileged" />
@@ -4539,16 +4547,25 @@
<!-- Allows application to request to stream content from an Android host to a nearby device
({@link android.companion.AssociationRequest#DEVICE_PROFILE_NEARBY_DEVICE_STREAMING})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING"
android:protectionLevel="signature|privileged" />
+ <!-- Allows application to request to stream content from an Android host to a nearby device
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_SENSOR_DEVICE_STREAMING})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows application to request to be associated with a vehicle head unit capable of
automotive projection
({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
android:protectionLevel="internal|role" />
@@ -4557,7 +4574,7 @@
and/or data with other devices, such as notifications, photos and media
({@link android.companion.AssociationRequest#DEVICE_PROFILE_COMPUTER})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER"
android:protectionLevel="signature|privileged" />
@@ -5631,11 +5648,21 @@
<!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
state.
- <p>Protection level: signature|role
- <p>Intended for use by ROLE_ASSISTANT and signature apps only.
+ <p>Protection level: signature|module|role
+ <p>Intended for use by ROLE_ASSISTANT, VDM, and signature apps only.
-->
<permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
- android:protectionLevel="signature|module|role"/>
+ android:protectionLevel="signature|module|role"
+ android:featureFlag="!android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
+
+ <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
+ state.
+ <p>Protection level: signature|privileged|module|role
+ <p>Intended for use by ROLE_ASSISTANT, VDM, and signature / privileged apps only.
+ -->
+ <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+ android:protectionLevel="signature|privileged|module|role"
+ android:featureFlag="android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
@@ -6845,6 +6872,13 @@
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
+ <!-- @SystemApi @hide Allows an application to collect high-precision PowerMonitor readings
+ <p>Protection level: signature|privileged|development
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION) -->
+ <permission android:name="android.permission.ACCESS_FINE_POWER_MONITORS"
+ android:protectionLevel="signature|privileged|development"
+ android:featureFlag="android.permission.flags.fine_power_monitor_permission" />
+
<!--Allows an application to manage statscompanion.
<p>Not for use by third-party applications.
@hide -->
@@ -7034,6 +7068,13 @@
<permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows for reading subscription plan fields for status and end date.
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ -->
+ <permission android:name="android.permission.READ_SUBSCRIPTION_PLANS"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date" />
+
<!-- C2DM permission.
@hide Used internally.
-->
@@ -8266,20 +8307,21 @@
android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows a trusted application to perform actions on behalf of users inside of
+ <!-- Allows a trusted application to perform actions on behalf of users inside of
applications with privacy guarantees from the system.
<p>This permission is currently only granted to system packages in the
{@link android.app.role.SYSTEM_UI_INTELLIGENCE} role which complies with privacy
requirements outlined in the Android CDD section "9.8.6 Content Capture".
<p>Apps are not able to opt-out from caller having this permission.
<p>Protection level: internal|role
+ @SystemApi
@hide
- @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") -->
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) -->
<permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
android:protectionLevel="internal|role" />
- <!-- @SystemApi Allows an application to perform actions on behalf of users inside of
+ <!-- Allows an application to perform actions on behalf of users inside of
applications.
<p>This permission is currently only granted to preinstalled / system apps having the
{@link android.app.role.ASSISTANT} role.
@@ -8287,8 +8329,7 @@
limiting to only callers with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}
instead.
<p>Protection level: internal|role
- @hide
- @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") -->
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) -->
<permission android:name="android.permission.EXECUTE_APP_FUNCTIONS"
android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
android:protectionLevel="internal|role" />
@@ -8777,6 +8818,17 @@
android:protectionLevel="signature|privileged|vendorPrivileged"
android:featureFlag="android.media.tv.flags.kids_mode_tvdb_sharing"/>
+ <!-- @SystemApi
+ @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled")
+ This permission is required to access the specific text classifier you need from the
+ TextClassificationManager.
+ <p>Protection level: signature|role
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml b/core/res/res/color/btn_material_filled_background_color_watch.xml
index 59810356c3b4..78547bc52f00 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
+++ b/core/res/res/color/btn_material_filled_background_color_watch.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:color="?attr/materialColorOnSurface" />
+ android:alpha="@dimen/disabled_alpha_wear_material3"
+ android:color="@color/materialColorOnSurface" />
+ <item android:color="@color/materialColorPrimary" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color/btn_material_filled_content_color_watch.xml
index 70aace4e7d76..3a4ec3cff935 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ b/core/res/res/color/btn_material_filled_content_color_watch.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:color="?attr/materialColorPrimary" />
+ android:alpha="@dimen/primary_content_alpha_wear_material3"
+ android:color="@color/materialColorOnSurface" />
+ <item android:color="@color/materialColorOnPrimary" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_content_color.xml b/core/res/res/color/btn_material_filled_tonal_background_color_watch.xml
index 4cc8fe5ecb91..e9b7e4c286bd 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
+++ b/core/res/res/color/btn_material_filled_tonal_background_color_watch.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:color="?attr/materialColorOnPrimary" />
+ android:alpha="@dimen/disabled_alpha_wear_material3"
+ android:color="@color/materialColorOnSurface" />
+ <item android:color="@color/materialColorSurfaceContainer" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color/btn_material_filled_tonal_content_color_watch.xml
index b2a25af0d670..8b5deda05745 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ b/core/res/res/color/btn_material_filled_tonal_content_color_watch.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:color="?attr/materialColorSurfaceContainer" />
+ android:alpha="@dimen/primary_content_alpha_wear_material3"
+ android:color="@color/materialColorOnSurface" />
+ <item android:color="@color/materialColorOnSurface" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_outlined_background_color.xml b/core/res/res/color/btn_material_outlined_background_color_watch.xml
index 665f47faca0d..f9c9a3f82341 100644
--- a/core/res/res/color-watch-v36/btn_material_outlined_background_color.xml
+++ b/core/res/res/color/btn_material_outlined_background_color_watch.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:color="?attr/materialColorOutline" />
+ android:alpha="@dimen/disabled_alpha_wear_material3"
+ android:color="@color/materialColorOnSurface" />
+ <item android:color="@color/materialColorOutline" />
</selector>
diff --git a/core/res/res/color/input_method_switch_on_item.xml b/core/res/res/color/input_method_switch_on_item.xml
index 49fe0815c757..f38e0ac3454e 100644
--- a/core/res/res/color/input_method_switch_on_item.xml
+++ b/core/res/res/color/input_method_switch_on_item.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_activated="true" android:color="?attr/materialColorOnSecondaryContainer" />
- <item android:color="?attr/materialColorOnSurface" />
+ <item android:state_activated="true" android:color="@color/materialColorOnSecondaryContainer" />
+ <item android:color="@color/materialColorOnSurface" />
</selector>
diff --git a/core/res/res/color/notification_expand_button_state_tint.xml b/core/res/res/color/notification_expand_button_state_tint.xml
index 5a8594f0e461..3409a2c7ab14 100644
--- a/core/res/res/color/notification_expand_button_state_tint.xml
+++ b/core/res/res/color/notification_expand_button_state_tint.xml
@@ -16,9 +16,9 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_pressed="true" android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+ <item android:state_pressed="true" android:color="@androidprv:color/materialColorOnPrimaryFixed"
android:alpha="0.15"/>
- <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+ <item android:state_hovered="true" android:color="@androidprv:color/materialColorOnPrimaryFixed"
android:alpha="0.11"/>
<item android:color="@color/transparent" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color/system_on_surface_disabled.xml b/core/res/res/color/system_on_surface_disabled.xml
index aba87f543c44..039ab024a32a 100644
--- a/core/res/res/color/system_on_surface_disabled.xml
+++ b/core/res/res/color/system_on_surface_disabled.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?attr/materialColorOnSurface"
+ <item android:color="@color/materialColorOnSurface"
android:alpha="?attr/disabledAlpha" />
</selector>
diff --git a/core/res/res/color/system_outline_disabled.xml b/core/res/res/color/system_outline_disabled.xml
index 0a67ce3bf186..b5a6b6bd8e25 100644
--- a/core/res/res/color/system_outline_disabled.xml
+++ b/core/res/res/color/system_outline_disabled.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?attr/materialColorOutline"
+ <item android:color="@color/materialColorOutline"
android:alpha="?attr/disabledAlpha" />
</selector>
diff --git a/core/res/res/color/system_surface_disabled.xml b/core/res/res/color/system_surface_disabled.xml
index 2d7fe7d727be..157227241d44 100644
--- a/core/res/res/color/system_surface_disabled.xml
+++ b/core/res/res/color/system_surface_disabled.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?attr/materialColorSurface"
+ <item android:color="@color/materialColorSurface"
android:alpha="?attr/disabledAlpha" />
</selector>
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable/btn_background_material_filled_tonal_watch.xml
index fbd697371329..69c467b19fa0 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
+++ b/core/res/res/drawable/btn_background_material_filled_tonal_watch.xml
@@ -18,7 +18,7 @@
android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+ <solid android:color="@color/btn_material_filled_tonal_background_color_watch"/>
<corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
<size
android:width="@dimen/btn_material_width"
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable/btn_background_material_filled_watch.xml
index 6e74f64fea60..76ba84724f0b 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
+++ b/core/res/res/drawable/btn_background_material_filled_watch.xml
@@ -18,7 +18,7 @@
android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_background_color"/>
+ <solid android:color="@color/btn_material_filled_background_color_watch"/>
<corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
<size
android:width="@dimen/btn_material_width"
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_outlined.xml b/core/res/res/drawable/btn_background_material_outlined_watch.xml
index 7bc40604dc25..16190aa0279f 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_outlined.xml
+++ b/core/res/res/drawable/btn_background_material_outlined_watch.xml
@@ -30,7 +30,7 @@
<corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
<stroke
android:width="1dp"
- android:color="@color/btn_material_outlined_background_color" />
+ android:color="@color/btn_material_outlined_background_color_watch" />
<size
android:width="@dimen/btn_material_width"
android:height="@dimen/btn_material_height" />
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_text.xml b/core/res/res/drawable/btn_background_material_text_watch.xml
index 145685c8095c..145685c8095c 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_text.xml
+++ b/core/res/res/drawable/btn_background_material_text_watch.xml
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable/dialog_alert_button_background_negative_watch.xml
index 0314bbec2b55..495fa4aabe3a 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
+++ b/core/res/res/drawable/dialog_alert_button_background_negative_watch.xml
@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+ <solid android:color="@color/btn_material_filled_tonal_background_color_watch"/>
<corners android:radius="@dimen/config_wearMaterial3_bottomDialogCornerRadius" />
<size
android:width="@dimen/dialog_btn_negative_width"
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml b/core/res/res/drawable/dialog_alert_button_background_positive_watch.xml
index 92262fb6d89d..20a4c0b6db90 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
+++ b/core/res/res/drawable/dialog_alert_button_background_positive_watch.xml
@@ -21,7 +21,7 @@
android:pivotX="50%"
android:pivotY="50%">
<shape android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_background_color"/>
+ <solid android:color="@color/btn_material_filled_background_color_watch"/>
<corners android:radius="200dp" />
<size
android:width="63dp"
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml b/core/res/res/drawable/dialog_alert_button_negative_watch.xml
index c155ba1ba077..1776962dbfaf 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
+++ b/core/res/res/drawable/dialog_alert_button_negative_watch.xml
@@ -15,8 +15,8 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dialog_alert_button_background_negative"/>
+ <item android:drawable="@drawable/dialog_alert_button_background_negative_watch"/>
<item
- android:drawable="@drawable/ic_close"
+ android:drawable="@drawable/ic_close_watch"
android:gravity="center" />
</layer-list> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml b/core/res/res/drawable/dialog_alert_button_positive_watch.xml
index 01ab0734d8e8..ab08e2a1d67d 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
+++ b/core/res/res/drawable/dialog_alert_button_positive_watch.xml
@@ -15,8 +15,8 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+ <item android:drawable="@drawable/dialog_alert_button_background_positive_watch"/>
<item
- android:drawable="@drawable/ic_check"
+ android:drawable="@drawable/ic_check_watch"
android:gravity="center" />
</layer-list> \ No newline at end of file
diff --git a/core/res/res/drawable/floating_popup_background.xml b/core/res/res/drawable/floating_popup_background.xml
index 99acedf06e2d..7200954140b7 100644
--- a/core/res/res/drawable/floating_popup_background.xml
+++ b/core/res/res/drawable/floating_popup_background.xml
@@ -18,7 +18,7 @@
<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:attr/materialColorSurfaceContainerHighest"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
diff --git a/core/res/res/drawable-watch-v36/ic_check.xml b/core/res/res/drawable/ic_check_watch.xml
index 7b01e64ffdd3..2fc161fbbdb6 100644
--- a/core/res/res/drawable-watch-v36/ic_check.xml
+++ b/core/res/res/drawable/ic_check_watch.xml
@@ -19,7 +19,7 @@
android:height="28dp"
android:viewportWidth="960"
android:viewportHeight="960"
- android:tint="@color/btn_material_filled_content_color">
- <path android:fillColor="@color/btn_material_filled_content_color"
+ android:tint="@color/btn_material_filled_content_color_watch">
+ <path android:fillColor="@color/btn_material_filled_content_color_watch"
android:pathData="M382,597.87L716.7,263.17Q730.37,249.5 748.76,249.5Q767.15,249.5 780.83,263.17Q794.5,276.85 794.5,295.62Q794.5,314.39 780.83,328.07L414.07,695.59Q400.39,709.26 382,709.26Q363.61,709.26 349.93,695.59L178.41,524.07Q164.74,510.39 165.12,491.62Q165.5,472.85 179.17,459.17Q192.85,445.5 211.62,445.5Q230.39,445.5 244.07,459.17L382,597.87Z"/>
</vector>
diff --git a/core/res/res/drawable-watch-v36/ic_close.xml b/core/res/res/drawable/ic_close_watch.xml
index 1f3da367ac3f..55e721337f14 100644
--- a/core/res/res/drawable-watch-v36/ic_close.xml
+++ b/core/res/res/drawable/ic_close_watch.xml
@@ -19,7 +19,7 @@
android:height="28dp"
android:viewportWidth="960"
android:viewportHeight="960"
- android:tint="@color/btn_material_filled_tonal_content_color">
- <path android:fillColor="@color/btn_material_filled_tonal_content_color"
+ android:tint="@color/btn_material_filled_tonal_content_color_watch">
+ <path android:fillColor="@color/btn_material_filled_tonal_content_color_watch"
android:pathData="M480,543.65L287.83,735.83Q275.15,748.5 256,748.5Q236.85,748.5 224.17,735.83Q211.5,723.15 211.5,704Q211.5,684.85 224.17,672.17L416.35,480L224.17,287.83Q211.5,275.15 211.5,256Q211.5,236.85 224.17,224.17Q236.85,211.5 256,211.5Q275.15,211.5 287.83,224.17L480,416.35L672.17,224.17Q684.85,211.5 704,211.5Q723.15,211.5 735.83,224.17Q748.5,236.85 748.5,256Q748.5,275.15 735.83,287.83L543.65,480L735.83,672.17Q748.5,684.85 748.5,704Q748.5,723.15 735.83,735.83Q723.15,748.5 704,748.5Q684.85,748.5 672.17,735.83L480,543.65Z"/>
</vector>
diff --git a/core/res/res/drawable/immersive_cling_bg.xml b/core/res/res/drawable/immersive_cling_bg.xml
index de29c32390e1..b28a423ea06b 100644
--- a/core/res/res/drawable/immersive_cling_bg.xml
+++ b/core/res/res/drawable/immersive_cling_bg.xml
@@ -20,5 +20,5 @@
<corners
android:bottomLeftRadius="28dp"
android:bottomRightRadius="28dp"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape>
diff --git a/core/res/res/drawable/input_method_switch_button.xml b/core/res/res/drawable/input_method_switch_button.xml
index 396d81ed87f6..1ee9b81a855a 100644
--- a/core/res/res/drawable/input_method_switch_button.xml
+++ b/core/res/res/drawable/input_method_switch_button.xml
@@ -30,7 +30,7 @@
<shape android:shape="rectangle">
<corners android:radius="28dp"/>
<solid android:color="@color/transparent"/>
- <stroke android:color="?attr/materialColorPrimary"
+ <stroke android:color="@color/materialColorPrimary"
android:width="1dp"/>
<padding android:left="16dp"
android:top="8dp"
diff --git a/core/res/res/drawable/input_method_switch_item_background.xml b/core/res/res/drawable/input_method_switch_item_background.xml
index eb7a24691f37..ce5b6f92e539 100644
--- a/core/res/res/drawable/input_method_switch_item_background.xml
+++ b/core/res/res/drawable/input_method_switch_item_background.xml
@@ -29,7 +29,7 @@
<item android:state_activated="true">
<shape android:shape="rectangle">
<corners android:radius="28dp"/>
- <solid android:color="?attr/materialColorSecondaryContainer"/>
+ <solid android:color="@color/materialColorSecondaryContainer"/>
</shape>
</item>
</selector>
diff --git a/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml b/core/res/res/drawable/progress_ring_watch.xml
index 5c0e5f606d81..8250ee600a8f 100644
--- a/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml
+++ b/core/res/res/drawable/progress_ring_watch.xml
@@ -27,7 +27,7 @@
android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio"
android:thickness="@dimen/progressbar_thickness"
android:useLevel="false">
- <solid android:color="?attr/materialColorSurfaceContainer"/>
+ <solid android:color="@color/materialColorSurfaceContainer"/>
</shape>
</item>
<item>
@@ -36,7 +36,7 @@
android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio"
android:thickness="@dimen/progressbar_thickness"
android:useLevel="true">
- <solid android:color="?attr/materialColorPrimary"/>
+ <solid android:color="@color/materialColorPrimary"/>
</shape>
</item>
</layer-list>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml b/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
deleted file mode 100644
index 8f7545690142..000000000000
--- a/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
+++ /dev/null
@@ -1,113 +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.
- -->
-
-<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
- Make sure to include all the existing id of the overridden alert_dialog_material.-->
-<com.android.internal.widget.WatchListDecorLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/parentPanel"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ScrollView
- android:id="@+id/scrollView"
- android:fillViewport="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <!-- Top Panel -->
- <FrameLayout
- android:paddingLeft="?dialogPreferredPadding"
- android:paddingRight="?dialogPreferredPadding"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/topPanel"
- android:minHeight="@dimen/dialog_list_padding_top_no_title">
- <include android:id="@+id/title_template"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/alert_dialog_title_material"/>
- </FrameLayout>
-
- <!-- Content Panel -->
- <FrameLayout android:id="@+id/contentPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipToPadding="false">
- <TextView android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal|top"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
- android:paddingStart="?dialogPreferredPadding"
- android:paddingEnd="?dialogPreferredPadding"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"/>
- </FrameLayout>
-
- <!-- Custom Panel, to replace content panel if needed -->
- <FrameLayout android:id="@+id/customPanel"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minHeight="64dp">
- <FrameLayout android:id="@+android:id/custom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <!-- Button Panel -->
- <FrameLayout
- android:id="@+id/buttonPanel"
- android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
- android:layout_weight="1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:orientation="vertical"
- android:paddingBottom="?dialogPreferredPadding"
- android:measureWithLargestChild="true">
- <Button android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:layout_weight="1"
- style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"/>
- <Button android:id="@+id/button3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:layout_weight="1"
- style="?android:attr/buttonBarButtonStyle"/>
- <Button android:id="@+id/button1"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:layout_weight="1"
- style="@*android:style/Widget.DeviceDefault.Button.Filled"/>
- </LinearLayout>
- </FrameLayout>
- </LinearLayout>
- </ScrollView>
-</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml b/core/res/res/layout/alert_dialog_icon_button_watch.xml
index 407ec7a42740..e5bc86c7bc6c 100644
--- a/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml
+++ b/core/res/res/layout/alert_dialog_icon_button_watch.xml
@@ -114,7 +114,7 @@
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:src="@drawable/dialog_alert_button_positive"/>
+ android:src="@drawable/dialog_alert_button_positive_watch"/>
</FrameLayout>
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/layout/alert_dialog_title_watch.xml b/core/res/res/layout/alert_dialog_title_watch.xml
new file mode 100644
index 000000000000..9c5148ffbd7a
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_title_watch.xml
@@ -0,0 +1,46 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="top|center_horizontal">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:adjustViewBounds="true">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="8dp"
+ android:maxHeight="32dp"
+ android:maxWidth="32dp"
+ android:src="@null" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/alertTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/alertDialog_material_side_margin_title"
+ android:layout_marginEnd="@dimen/alertDialog_material_side_margin_title"
+ android:textAppearance="@style/TextAppearance.AlertDialog.Title"
+ android:gravity="center" />
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_watch.xml b/core/res/res/layout/alert_dialog_watch.xml
new file mode 100644
index 000000000000..cb81fc5976a0
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_watch.xml
@@ -0,0 +1,138 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
+ Make sure to include all the existing id of the overridden alert_dialog_material.-->
+<com.android.internal.widget.WatchListDecorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <requestFocus />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/alertDialog_material_side_margin"
+ android:layout_marginEnd="@dimen/alertDialog_material_side_margin"
+ android:gravity="center_vertical">
+
+ <!-- Top Spacer -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/alertDialog_material_top_margin" />
+
+ <!-- Top Panel -->
+ <FrameLayout
+ android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/dialog_list_padding_top_no_title">
+
+ <include
+ android:id="@+id/title_template"
+ layout="@layout/alert_dialog_title_watch"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <!-- Content Panel -->
+ <FrameLayout
+ android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/alertDialog_material_side_margin_body"
+ android:layout_marginEnd="@dimen/alertDialog_material_side_margin_body"
+ android:textAppearance="@style/TextAppearance.AlertDialog.Body1"
+ android:gravity="center_horizontal|top" />
+ </FrameLayout>
+
+ <!-- Custom Panel, to replace content panel if needed -->
+ <FrameLayout
+ android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minHeight="64dp">
+
+ <FrameLayout
+ android:id="@+android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <!-- Button Panel -->
+ <FrameLayout
+ android:id="@+id/buttonPanel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_gravity="bottom"
+ android:orientation="vertical">
+ <!-- Positive Button -->
+ <Button
+ android:id="@+id/button1"
+ style="@*android:style/Widget.DeviceDefault.Button.Filled"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center" />
+ <!--Neutral Button -->
+ <Button
+ android:id="@+id/button3"
+ style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center" />
+ <!-- Negative Button -->
+ <Button
+ android:id="@+id/button2"
+ style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_gravity="center"
+ android:gravity="center" />
+ </LinearLayout>
+ </FrameLayout>
+
+ <!-- Bottom Spacer -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/alertDialog_material_bottom_margin" />
+
+ </LinearLayout>
+ </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index 0b3861cad252..f6e6adfb4b28 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -54,7 +54,7 @@
android:ellipsize="end"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textSize="@dimen/floating_toolbar_text_size"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:background="@null"
android:focusable="false"
android:focusableInTouchMode="false"
diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml
index a51836b35057..5fd774f9c3af 100644
--- a/core/res/res/layout/floating_popup_overflow_button.xml
+++ b/core/res/res/layout/floating_popup_overflow_button.xml
@@ -26,4 +26,4 @@
android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
android:scaleType="centerInside"
android:background="?attr/actionBarItemBackground"
- android:tint="?androidprv:attr/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface" />
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index 2cde9e648276..4e869b76b510 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -42,7 +42,7 @@
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:text="@string/immersive_cling_title"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="24sp"
android:fontFamily="google-sans" />
@@ -54,7 +54,7 @@
android:paddingTop="14dp"
android:gravity="center_horizontal"
android:text="@string/immersive_cling_description"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textSize="14sp"
android:fontFamily="google-sans" />
@@ -72,7 +72,7 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:text="@string/immersive_cling_positive"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
android:textAllCaps="false"
android:textSize="14sp"
android:textFontWeight="500"
diff --git a/core/res/res/layout/input_method_switch_item_divider.xml b/core/res/res/layout/input_method_switch_item_divider.xml
index 4f8c963ff8cf..b56cfb7bf6bd 100644
--- a/core/res/res/layout/input_method_switch_item_divider.xml
+++ b/core/res/res/layout/input_method_switch_item_divider.xml
@@ -26,7 +26,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
- android:background="?attr/materialColorOutlineVariant"
+ android:background="@color/materialColorOutlineVariant"
android:layout_marginStart="20dp"
android:layout_marginEnd="24dp"
android:importantForAccessibility="no"/>
diff --git a/core/res/res/layout/input_method_switch_item_header.xml b/core/res/res/layout/input_method_switch_item_header.xml
index f0080a630025..05c73d04d715 100644
--- a/core/res/res/layout/input_method_switch_item_header.xml
+++ b/core/res/res/layout/input_method_switch_item_header.xml
@@ -33,6 +33,6 @@
android:fontFamily="google-sans-text"
android:textAppearance="?attr/textAppearance"
android:accessibilityHeading="true"
- android:textColor="?attr/materialColorPrimary"/>
+ android:textColor="@color/materialColorPrimary"/>
</LinearLayout>
diff --git a/core/res/res/layout/input_method_switch_item_new.xml b/core/res/res/layout/input_method_switch_item_new.xml
index 7b241aff3fb1..368860848f5d 100644
--- a/core/res/res/layout/input_method_switch_item_new.xml
+++ b/core/res/res/layout/input_method_switch_item_new.xml
@@ -56,7 +56,7 @@
android:marqueeRepeatLimit="1"
android:singleLine="true"
android:fontFamily="google-sans-text"
- android:textColor="?attr/materialColorOnSurfaceVariant"
+ android:textColor="@color/materialColorOnSurfaceVariant"
android:textAppearance="?attr/textAppearanceListItemSecondary"
android:textAllCaps="true"
android:visibility="gone"/>
diff --git a/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml
index b25adaabf8e8..68eafee03848 100644
--- a/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml
+++ b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml
@@ -18,14 +18,14 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/conversation_face_pile"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:forceHasOverlappingRendering="false"
>
<ImageView
android:id="@+id/conversation_face_pile_top"
- android:layout_width="@dimen/messaging_avatar_size"
- android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_width="@dimen/notification_2025_face_pile_avatar_size"
+ android:layout_height="@dimen/notification_2025_face_pile_avatar_size"
android:scaleType="centerCrop"
android:layout_gravity="end|top"
android:background="@drawable/notification_icon_circle"
@@ -43,8 +43,8 @@
/>
<ImageView
android:id="@+id/conversation_face_pile_bottom"
- android:layout_width="@dimen/messaging_avatar_size"
- android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_width="@dimen/notification_2025_face_pile_avatar_size"
+ android:layout_height="@dimen/notification_2025_face_pile_avatar_size"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:background="@drawable/notification_icon_circle"
diff --git a/core/res/res/layout/notification_2025_conversation_header.xml b/core/res/res/layout/notification_2025_conversation_header.xml
new file mode 100644
index 000000000000..db79e79c96df
--- /dev/null
+++ b/core/res/res/layout/notification_2025_conversation_header.xml
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<com.android.internal.widget.ConversationHeaderLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="@dimen/notification_2025_margin"
+ >
+
+ <TextView
+ android:id="@+id/conversation_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="16sp"
+ android:singleLine="true"
+ android:layout_weight="1"
+ />
+
+ <TextView
+ android:id="@+id/app_name_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <!-- App Name -->
+ <com.android.internal.widget.ObservableTextView
+ android:id="@+id/app_name_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/time_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <DateTimeView
+ android:id="@+id/time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ViewStub
+ android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout="@layout/notification_template_part_chronometer"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/verification_icon"
+ android:layout_width="@dimen/notification_verification_icon_size"
+ android:layout_height="@dimen/notification_verification_icon_size"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:baseline="10dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_weight="100"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageButton
+ android:id="@+id/feedback"
+ android:layout_width="@dimen/notification_feedback_size"
+ android:layout_height="@dimen/notification_feedback_size"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:contentDescription="@string/notification_feedback_indicator"
+ android:baseline="13dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_feedback_indicator"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/phishing_alert"
+ android:layout_width="@dimen/notification_phishing_alert_size"
+ android:layout_height="@dimen/notification_phishing_alert_size"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:baseline="10dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_dialog_alert_material"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_phishing_alert_content_description"
+ />
+
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:baseline="10dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
+ />
+
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@dimen/notification_alerted_size"
+ android:layout_height="@dimen/notification_alerted_size"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:baseline="10dp"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+</com.android.internal.widget.ConversationHeaderLinearLayout>
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 90befd911bdf..7ec2450ceb71 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -18,32 +18,27 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/conversation_icon_container"
- android:layout_width="@dimen/conversation_content_start"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
android:layout_height="wrap_content"
android:gravity="start|top"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingTop="20dp"
- android:paddingBottom="16dp"
android:importantForAccessibility="no"
>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_margin="@dimen/notification_2025_margin"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_gravity="top|center_horizontal"
>
- <!-- Big icon: 48x48, 12dp padding top, 16dp padding sides -->
<com.android.internal.widget.CachingIconView
android:id="@+id/conversation_icon"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:layout_marginLeft="@dimen/conversation_badge_protrusion"
- android:layout_marginRight="@dimen/conversation_badge_protrusion"
- android:layout_marginBottom="@dimen/conversation_badge_protrusion"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:background="@drawable/notification_icon_circle"
android:clipToOutline="true"
android:scaleType="centerCrop"
@@ -52,19 +47,25 @@
<ViewStub
android:layout="@layout/notification_2025_conversation_face_pile_layout"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:layout_marginLeft="@dimen/conversation_badge_protrusion"
- android:layout_marginRight="@dimen/conversation_badge_protrusion"
- android:layout_marginBottom="@dimen/conversation_badge_protrusion"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:id="@+id/conversation_face_pile"
/>
+ <!-- The badge icon is visually aligned to the square containing the conversation icon,
+ but it has a border in the color of the background that is meant to delimit it from the
+ conversation icon. This border, although not visible due to the color, is technically
+ outside these bounds.
+ In order to align the badge properly to the bottom end of the square, we use a top/start
+ margin that is equal to (size of the conversation icon - size of the badge - size of the
+ border on one side).
+ -->
<FrameLayout
android:id="@+id/conversation_icon_badge"
- android:layout_width="@dimen/conversation_icon_size_badged"
- android:layout_height="@dimen/conversation_icon_size_badged"
- android:layout_gravity="end|bottom"
+ android:layout_width="@dimen/notification_2025_conversation_icon_badge_size"
+ android:layout_height="@dimen/notification_2025_conversation_icon_badge_size"
+ android:layout_marginTop="@dimen/notification_2025_conversation_icon_badge_position"
+ android:layout_marginStart="@dimen/notification_2025_conversation_icon_badge_position"
android:clipChildren="false"
android:clipToPadding="false"
>
@@ -83,7 +84,7 @@
android:id="@+id/icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_margin="4dp"
+ android:layout_margin="@dimen/notification_2025_conversation_icon_badge_padding"
android:layout_gravity="center"
android:forceHasOverlappingRendering="false"
/>
diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml
index c1b491fc4b0e..ecaf0b9a785f 100644
--- a/core/res/res/layout/notification_2025_messaging_group.xml
+++ b/core/res/res/layout/notification_2025_messaging_group.xml
@@ -24,13 +24,13 @@
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/message_icon_container"
- android:layout_width="@dimen/conversation_content_start"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
android:layout_height="wrap_content">
<ImageView
android:layout_gravity="top|center_horizontal"
android:id="@+id/message_icon"
- android:layout_width="@dimen/messaging_avatar_size"
- android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:background="@drawable/notification_icon_circle"
android:clipToOutline="true"
android:scaleType="centerCrop"
@@ -58,14 +58,14 @@
</com.android.internal.widget.RemeasuringLinearLayout>
<FrameLayout
android:id="@+id/messaging_group_icon_container"
- android:layout_width="@dimen/messaging_avatar_size"
- android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_marginStart="12dp"
android:visibility="gone"/>
<FrameLayout
android:id="@+id/messaging_group_sending_progress_container"
android:layout_width="@dimen/messaging_group_sending_progress_size"
- android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_marginStart="12dp"
android:layout_gravity="top"
android:visibility="gone">
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 09c02c9994f4..76c810bdb2c1 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -28,8 +28,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -41,8 +41,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 614444d6b2f0..c4bca1142ece 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -41,13 +41,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:orientation="vertical"
android:paddingBottom="@dimen/notification_2025_margin"
>
<include
- layout="@layout/notification_template_conversation_header"
+ layout="@layout/notification_2025_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
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 f539105368e7..2e0a7afc3cd1 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -32,8 +32,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -45,8 +45,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index ddf3ebceaa46..f644adefda9d 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -46,8 +46,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -59,8 +59,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
index 0c4c7fba90b1..f31f65e90950 100644
--- a/core/res/res/layout/notification_2025_template_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_conversation.xml
@@ -60,11 +60,11 @@
<!-- Use layout_marginStart instead of paddingStart to work around strange
measurement behavior on lower display densities. -->
<include
- layout="@layout/notification_template_conversation_header"
+ layout="@layout/notification_2025_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
- android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
/>
<!-- Messages -->
@@ -86,7 +86,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_content_margin"
- android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:layout_marginEnd="@dimen/notification_content_margin_end" />
<include layout="@layout/notification_material_action_list" />
</com.android.internal.widget.RemeasuringLinearLayout>
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 3ff71b78835d..2af0ec2972df 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -49,13 +49,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:orientation="vertical"
android:minHeight="68dp"
>
<include
- layout="@layout/notification_template_conversation_header"
+ layout="@layout/notification_2025_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
@@ -97,7 +97,7 @@
layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:layout_marginTop="@dimen/notification_content_margin"
/>
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 5b5872657a43..e3c201465eb0 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -33,7 +33,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:layout_marginTop="@dimen/notification_2025_header_height"
android:clipChildren="false"
android:orientation="vertical">
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index b7fe454e09d4..fc727e1c72f5 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -20,7 +20,6 @@
android:id="@+id/notification_header"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_2025_header_height"
- android:layout_marginBottom="@dimen/notification_header_margin_bottom"
android:clipChildren="false"
android:gravity="center_vertical"
android:orientation="horizontal"
@@ -33,8 +32,7 @@
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -47,8 +45,7 @@
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
diff --git a/core/res/res/layout/notification_2025_text.xml b/core/res/res/layout/notification_2025_text.xml
index 48b1083a5e53..474f6d2099c6 100644
--- a/core/res/res/layout/notification_2025_text.xml
+++ b/core/res/res/layout/notification_2025_text.xml
@@ -21,6 +21,7 @@
android:layout_height="@dimen/notification_text_height"
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_text_margin_top"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:gravity="top"
android:maxLines="1"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bfa3b067ead0..4d96adeb8e09 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Beller-ID se verstek is beperk. Volgende oproep: nie beperk nie"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Beller-ID se verstek is nie beperk nie. Volgende oproep: beperk"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID se verstek is nie beperk nie. Volgende oproep: nie beperk nie"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Hierdie app is nie versoenbaar met 16 KB nie. APK-belyningkontrole het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; vir meer inligting"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Hierdie app is nie versoenbaar met 16 KB nie. ELF-belyningkontrole het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; vir meer inligting"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Hierdie app is nie versoenbaar met 16 KB nie. APK- ELF-belyningkontroles het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; vir meer inligting"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Diens nie verskaf nie."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Jy kan nie die beller-ID-instelling verander nie."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Het data oorgeskakel na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -308,7 +305,7 @@
<string name="notification_channel_alerts" msgid="5070241039583668427">"Opletberigte"</string>
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Kleinhandeldemonstrasie"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string>
- <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Program loop tans"</string>
+ <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App loop tans"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programme wat batterykrag gebruik"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Vergroting"</string>
<string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Toeganklikheidgebruik"</string>
@@ -372,69 +369,69 @@
<string name="permlab_statusBar" msgid="8798267849526214017">"deaktiveer of verander statusbalk"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Laat die app toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"wees die statusbalk"</string>
- <string name="permdesc_statusBarService" msgid="6652917399085712557">"Laat die program toe om die statusbalk te wees."</string>
+ <string name="permdesc_statusBarService" msgid="6652917399085712557">"Laat die app toe om die statusbalk te wees."</string>
<string name="permlab_expandStatusBar" msgid="1184232794782141698">"vou statusbalk in of uit"</string>
- <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Laat die program toe om die statusbalk uit te vou of in te vou."</string>
+ <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Laat die app toe om die statusbalk uit te vou of in te vou."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"wys kennisgewings as volskermaktiwiteite op \'n geslote skerm"</string>
- <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Laat die program toe om kennisgewings as volskermaktiwiteite op \'n geslote toestel te wys"</string>
+ <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Laat die app toe om kennisgewings as volskermaktiwiteite op \'n geslote toestel te wys"</string>
<string name="permlab_install_shortcut" msgid="7451554307502256221">"installeer kortpaaie"</string>
- <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Stel \'n program in staat om Tuisskerm-kortpaaie by te voeg sonder gebruikerinmenging."</string>
+ <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Stel \'n app in staat om Tuisskerm-kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"deïnstalleer kortpaaie"</string>
- <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Laat die program toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
+ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Laat die app toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"herlei uitgaande oproepe"</string>
- <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Laat die program toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
+ <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Laat die app toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"antwoord foonoproepe"</string>
- <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Laat die program toe om inkomende foonoproepe te antwoord."</string>
+ <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Laat die app toe om inkomende foonoproepe te antwoord."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"ontvang teksboodskappe (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"Laat die program toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"Laat die app toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die app boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"ontvang teksboodskappe (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"Laat die program toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"Laat die app toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die app boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Stuur seluitsendingboodskappe aan"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die program toe om die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige programme kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die app toe om aan die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige apps kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Bestuur voortgaande oproepe"</string>
- <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n program in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string>
+ <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n app in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string>
<string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Kry toegang tot laaste bekende selidentiteit."</string>
<string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Laat ’n app toe om toegang tot die laaste bekende selidentiteit te kry wat deur telefonie verskaf is"</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lees seluitsending-boodskappe"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Laat die app toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige apps kan inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lees ingetekende nuus"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Laat die program toe om details oor die tans gesinkroniseerde strome te kry."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Laat die app toe om details oor die tans gesinkroniseerde strome te kry."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"SMS-boodskappe te stuur en te bekyk"</string>
- <string name="permdesc_sendSms" msgid="6757089798435130769">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
+ <string name="permdesc_sendSms" msgid="6757089798435130769">"Laat die app toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige apps kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
<string name="permlab_readSms" msgid="5164176626258800297">"lees jou teksboodskappe (SMS of MMS)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou tablet geberg is, lees."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Hierdie program kan alle SMS- (teks)-boodskappe lees wat op jou Android TV-toestel geberg is."</string>
- <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou foon geberg is, lees."</string>
+ <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Hierdie app kan alle SMS\'e (teksboodskappe) wat op jou tablet geberg is, lees."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Hierdie app kan alle SMS- (teks)-boodskappe lees wat op jou Android TV-toestel geberg is."</string>
+ <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Hierdie app kan alle SMS\'e (teksboodskappe) wat op jou foon geberg is, lees."</string>
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"ontvang teksboodskappe (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Laat die program toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Laat die app toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"haal lopende programme op"</string>
- <string name="permdesc_getTasks" msgid="7388138607018233726">"Laat die program toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die program toelaat om inligting oor watter programme op die toestel gebruik word, te ontdek."</string>
+ <string name="permdesc_getTasks" msgid="7388138607018233726">"Laat die app toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die app toelaat om inligting oor watter apps op die toestel gebruik word, te ontdek."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"bestuur profiel- en toesteleienaars"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Laat programme toe om die profieleienaars en die toesteleienaar te stel."</string>
<string name="permlab_reorderTasks" msgid="7598562301992923804">"herrangskik lopende programme"</string>
- <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Laat die program toe om take na die voorgrond of agtergrond te skuif. Die program kan dit moontlik sonder jou insette doen."</string>
+ <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Laat die app toe om take na die voorgrond of agtergrond te skuif. Die app kan dit moontlik sonder jou insette doen."</string>
<string name="permlab_enableCarMode" msgid="893019409519325311">"aktiveer motormodus"</string>
- <string name="permdesc_enableCarMode" msgid="56419168820473508">"Laat die program toe om die motormodus te aktiveer."</string>
+ <string name="permdesc_enableCarMode" msgid="56419168820473508">"Laat die app toe om die motormodus te aktiveer."</string>
<string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"maak ander programme toe"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Laat die program toe om agtergrondprosesse van ander programme te beëindig. Dit kan moontlik veroorsaak dat ander programme ophou werk."</string>
- <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Hierdie program kan bo-op ander programme verskyn"</string>
- <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Hierdie program kan bokant ander programme of ander dele van die skerm verskyn. Dit kan met normale programgebruik inmeng en die voorkoms van ander programme verander."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Laat die app toe om agtergrondprosesse van ander apps te beëindig. Dit kan moontlik veroorsaak dat ander apps ophou werk."</string>
+ <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Hierdie app kan bo-op ander apps verskyn"</string>
+ <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Hierdie app kan bokant ander apps of ander dele van die skerm verskyn. Dit kan met normale appgebruik inmeng en die voorkoms van ander apps verander."</string>
<string name="permlab_hideOverlayWindows" msgid="6382697828482271802">"versteek ander apps se oorleggers"</string>
<string name="permdesc_hideOverlayWindows" msgid="5660242821651958225">"Hierdie app kan versoek dat die stelsel oorleggers wat oorspronklik van apps af kom, versteek sodat hulle nie bo-op hulle wys nie."</string>
<string name="permlab_runInBackground" msgid="541863968571682785">"loop op die agtergrond"</string>
- <string name="permdesc_runInBackground" msgid="4344539472115495141">"Hierdie program kan op die agtergrond loop. Dit kan die battery vinniger laat pap word."</string>
+ <string name="permdesc_runInBackground" msgid="4344539472115495141">"Hierdie app kan op die agtergrond loop. Dit kan die battery vinniger laat pap word."</string>
<string name="permlab_useDataInBackground" msgid="783415807623038947">"gebruik data op die agtergrond"</string>
- <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Hierdie program kan data op die agtergrond gebruik. Dit kan die datagebruik vergroot."</string>
+ <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Hierdie app kan data op die agtergrond gebruik. Dit kan die datagebruik vergroot."</string>
<string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Skeduleer handelinge met presiese tydsbesturing"</string>
<string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Hierdie app kan werk skeduleer om op ’n gewenste tyd in die toekoms plaas te vind. Dit beteken ook dat die app kan werk wanneer die toestel nie aktief gebruik word nie."</string>
<string name="permlab_use_exact_alarm" msgid="348045139777131552">"Skeduleer wekkers of geleentheidonthounotas"</string>
<string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Hierdie app kan handelinge soos wekkers en onthounotas skeduleer om jou op ’n gewenste tyd in die toekoms in kennis te stel."</string>
- <string name="permlab_persistentActivity" msgid="464970041740567970">"laat program altyd loop"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Laat die program toe om dele van ditself deurdringend in die geheue te hou. Dit kan geheue wat aan ander programme beskikbaar is, beperk, en die tablet stadiger maak."</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Laat die program toe om dele daarvan in die geheue te laat voortbestaan. Dit kan geheue wat vir ander programme beskikbaar is, beperk en sodoende jou Android TV-toestel stadiger maak."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Laat die program toe om dele van ditself deurdringend in die geheue te hou. Dit kan geheue wat aan ander programme beskikbaar is, beperk, en die foon stadiger maak."</string>
+ <string name="permlab_persistentActivity" msgid="464970041740567970">"laat app altyd loop"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Laat die app toe om dele van die app deurdringend in die geheue te hou. Dit kan geheue wat aan ander apps beskikbaar is, beperk, en die tablet stadiger maak."</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Laat die app toe om dele daarvan in die geheue te laat voortbestaan. Dit kan geheue wat vir ander apps beskikbaar is, beperk en sodoende jou Android TV-toestel stadiger maak."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Laat die app toe om dele van die app deurdringend in die geheue te hou. Dit kan geheue wat aan ander apps beskikbaar is, beperk, en die foon stadiger maak."</string>
<string name="permlab_foregroundService" msgid="1768855976818467491">"laat loop voorgronddiens"</string>
- <string name="permdesc_foregroundService" msgid="8720071450020922795">"Laat die program toe om van voorgronddienste gebruik te maak."</string>
+ <string name="permdesc_foregroundService" msgid="8720071450020922795">"Laat die app toe om van voorgronddienste gebruik te maak."</string>
<string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"gebruik voorgronddienstipe “kamera”"</string>
<string name="permdesc_foregroundServiceCamera" msgid="6973701931250595727">"Laat die app toe om die voorgronddienstipe “kamera” te gebruik"</string>
<string name="permlab_foregroundServiceConnectedDevice" msgid="3019650546176872501">"gebruik voorgronddienstipe “gekoppelde toestel”"</string>
@@ -464,190 +461,190 @@
<string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"gebruik voorgronddienstipe “spesiale gebruik”"</string>
<string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"Laat die app toe om die voorgronddienstipe “spesiale gebruik” te gebruik"</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"meet programberging-ruimte"</string>
- <string name="permdesc_getPackageSize" msgid="742743530909966782">"Laat die program toe om sy kode, data en kasgroottes op te haal"</string>
+ <string name="permdesc_getPackageSize" msgid="742743530909966782">"Laat die app toe om sy kode, data en kasgroottes op te haal"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"verander stelsel-instellings"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"Laat die program toe om die stelsel se instellingsdata te verander. Kwaadwillige programme kan dalk jou stelsel se opstelling korrupteer."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"Laat die app toe om die stelsel se instellingsdata te verander. Kwaadwillige apps kan dalk jou stelsel se opstelling korrupteer."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"laat loop wanneer begin"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Laat die program toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die tablet om te begin, en dit laat die foon toe om die tablet stadiger te maak omdat dit altyd loop."</string>
- <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Laat die program toe om self te begin sodra die stelsel klaar geselflaai het. Dit kan dalk daartoe lei dat die toestel langer neem om jou Android TV-toestel te begin, en laat die program toe om die hele toestel stadiger te maak deur altyd te loop."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Laat die program toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die foon om te begin, en dit laat die foon toe om die foon stadiger te maak omdat dit altyd loop."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Laat die app toe om self te begin sodra die stelsel geselflaai het. Dit maak dat dit langer neem vir die tablet om te begin, en dit laat die foon toe om die tablet stadiger te maak omdat dit altyd loop."</string>
+ <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Laat die app toe om self te begin sodra die stelsel klaar geselflaai het. Dit kan dalk daartoe lei dat die toestel langer neem om jou Android TV-toestel te begin, en laat die app toe om die hele toestel stadiger te maak deur altyd te loop."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Laat die app toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die foon om te begin, en dit laat die foon toe om die foon stadiger te maak omdat dit altyd loop."</string>
<string name="permlab_broadcastSticky" msgid="4552241916400572230">"Stuur klewerige uitsending"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Laat die program toe om taai uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die tablet stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Laat die program toe om vaste uitsendings wat agterbly nadat die uitsending eindig, te stuur. Oormatige gebruik kan jou Android TV-toestel stadig of onstabiel maak omdat dit veroorsaak dat jou toestel te veel geheue gebruik."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Laat die program toe om taai uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die foon stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die tablet stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Laat die app toe om vaste uitsendings wat agterbly nadat die uitsending eindig, te stuur. Oormatige gebruik kan jou Android TV-toestel stadig of onstabiel maak omdat dit veroorsaak dat jou toestel te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die foon stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
<string name="permlab_readContacts" msgid="8776395111787429099">"lees jou kontakte"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Laat die program toe om data te lees oor jou kontakte wat op jou tablet geberg is. Programme sal ook toegang hê tot die rekeninge op jou tablet wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
- <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Laat die program toe om data te lees oor jou kontakte wat op jou Android TV-toestel geberg is. Programme sal ook toegang hê tot die rekeninge op jou Android TV-toestel wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
- <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Laat die program toe om data te lees oor jou kontakte wat op jou foon geberg is. Programme sal ook toegang hê tot die rekeninge op jou foon wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Laat die app toe om data te lees oor jou kontakte wat op jou tablet geberg is. Apps sal ook toegang hê tot die rekeninge op jou tablet wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Laat die app toe om data te lees oor jou kontakte wat op jou Android TV-toestel geberg is. Apps sal ook toegang hê tot die rekeninge op jou Android TV-toestel wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
+ <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Laat die app toe om data te lees oor jou kontakte wat op jou foon geberg is. Apps sal ook toegang hê tot die rekeninge op jou foon wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
<string name="permlab_writeContacts" msgid="8919430536404830430">"verander jou kontakte"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Laat die program toe om die data te wysig oor jou kontakte wat op jou tablet geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Laat die program toe om die data te wysig oor jou kontakte wat op jou Android TV-toestel geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
- <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Laat die program toe om die data te wysig oor jou kontakte wat op jou foon geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Laat die app toe om die data te wysig oor jou kontakte wat op jou tablet geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Laat die app toe om die data te wysig oor jou kontakte wat op jou Android TV-toestel geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Laat die app toe om die data te wysig oor jou kontakte wat op jou foon geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"lees oproeprekord"</string>
- <string name="permdesc_readCallLog" msgid="8964770895425873433">"Hierdie program kan jou oproepgeskiedenis lees."</string>
+ <string name="permdesc_readCallLog" msgid="8964770895425873433">"Hierdie app kan jou oproepgeskiedenis lees."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"skryf oproeprekord"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Laat die program toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die program toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
- <string name="permlab_bodySensors" msgid="662918578601619569">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program gebruik word"</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Laat die app toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die app toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die app toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
+ <string name="permlab_bodySensors" msgid="662918578601619569">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl app gebruik word"</string>
<string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Gee die app toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl die app gebruik word."</string>
- <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program op agtergrond is"</string>
- <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Gee die program toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl dit op die agtergrond is."</string>
+ <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl app op die agtergrond is"</string>
+ <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Gee die app toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl dit op die agtergrond is."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Lees kalendergebeurtenisse en -besonderhede"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie program kan alle kalendergeleenthede wat op jou Android TV-toestel geberg is, lees of jou kalenderdata stoor."</string>
- <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou foon geberg is of jou kalenderdata stoor."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie app kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie app kan alle kalendergeleenthede lees wat op jou Android TV-toestel geberg is of jou kalenderdata stoor."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Hierdie app kan alle kalendergebeurtenisse lees wat op jou foon geberg is of jou kalenderdata stoor."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"voeg by of verander kalenderafsprake en stuur \'n e-pos aan gaste sonder eienaars se medewete"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Hierdie program kan kalendergebeurtenisse op jou tablet byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Hierdie program kan kalendergeleenthede op jou Android TV-toestel byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of geleenthede verander sonder om hul eienaars in kennis te stel."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Hierdie program kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Hierdie app kan kalendergebeurtenisse op jou tablet byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Hierdie app kan kalendergeleenthede op jou Android TV-toestel byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of geleenthede verander sonder om hul eienaars in kennis te stel."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Hierdie app kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Gee die program toegang tot ekstra liggingverskaffer-bevele. Dit kan die program dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Gee die app toegang tot ekstra liggingverskaffer-bevele. Dit kan die app dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"kry net op die voorgrond toegang tot presiese ligging"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Hierdie program kan jou presiese ligging van liggingdienste af kry terwyl die program gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die program om die ligging te kry. Dit kan batterygebruik verhoog."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Hierdie app kan jou presiese ligging van liggingdienste af kry terwyl die app gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die app om die ligging te kry. Dit kan batterygebruik verhoog."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"kry benaderde ligging net op die voorgrond"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Hierdie program kan jou benaderde ligging van liggingdienste af kry terwyl die program gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die program om die ligging te kry."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Hierdie app kan jou benaderde ligging van liggingdienste af kry terwyl die app gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die app om die ligging te kry."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"kry ligging op die agtergrond"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Hierdie program kan op enige tydstip toegang tot ligging kry, selfs wanneer die program nie gebruik word nie."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Hierdie app kan op enige tydstip toegang tot ligging kry, selfs wanneer die app nie gebruik word nie."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"verander jou klankinstellings"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Laat die app toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"neem klank op"</string>
- <string name="permdesc_recordAudio" msgid="5857246765327514062">"Hierdie die program kan oudio met die mikrofoon opneem terwyl die program gebruik word."</string>
+ <string name="permdesc_recordAudio" msgid="5857246765327514062">"Hierdie die app kan oudio met die mikrofoon opneem terwyl die app gebruik word."</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"neem oudio op die agtergrond op"</string>
- <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Hierdie program kan enige tyd oudio met die mikrofoon opneem."</string>
+ <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Hierdie app kan enige tyd oudio met die mikrofoon opneem."</string>
<string name="permlab_detectScreenCapture" msgid="4447042362828799433">"bespeur skermskote van appvensters"</string>
<string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Hierdie app sal ingelig word as ’n skermskoot geneem word terwyl die app gebruik word."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"stuur bevele na die SIM"</string>
- <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die program toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
+ <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die app toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"herken fisieke aktiwiteit"</string>
- <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie program kan jou fisieke aktiwiteit herken."</string>
+ <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie app kan jou fisieke aktiwiteit herken."</string>
<string name="permlab_camera" msgid="6320282492904119413">"neem foto\'s en video\'s"</string>
- <string name="permdesc_camera" msgid="5240801376168647151">"Hierdie program kan met die kamera foto\'s neem en video\'s opneem terwyl die program gebruik word."</string>
+ <string name="permdesc_camera" msgid="5240801376168647151">"Hierdie app kan met die kamera foto\'s neem en video\'s opneem terwyl die app gebruik word."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"neem foto\'s en video\'s op die agtergrond"</string>
- <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie program kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
- <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n program of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
- <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselprogram kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die program ook die android.permission.CAMERA-toestemming het"</string>
- <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n program of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
- <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie program kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter program) of toegemaak word."</string>
+ <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie app kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
+ <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n app of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
+ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselapp kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die app ook die android.permission.CAMERA-toestemming het"</string>
+ <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n app of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
+ <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie app kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter app) of toegemaak word."</string>
<string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Laat ’n app of diens toe om toegang tot die kamera te verkry as ’n stelselgebruiker sonder koppelvlak."</string>
<string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Hierdie app het toegang tot die kamera as ’n stelselgebruiker sonder koppelvlak."</string>
<string name="permlab_vibrate" msgid="8596800035791962017">"beheer vibrasie"</string>
- <string name="permdesc_vibrate" msgid="8733343234582083721">"Laat die program toe om die vibrator te beheer."</string>
- <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Stel die program in staat om toegang tot die vibreerderstand te kry."</string>
+ <string name="permdesc_vibrate" msgid="8733343234582083721">"Laat die app toe om die vibrator te beheer."</string>
+ <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Stel die app in staat om toegang tot die vibreerderstand te kry."</string>
<string name="permlab_callPhone" msgid="1798582257194643320">"skakel foonnommers direk"</string>
<string name="permdesc_callPhone" msgid="7892422187827695656">"Laat die app toe om foonnommers sonder jou insae te bel. Dit kan onvoorsiene heffings of oproepe tot gevolg hê. Neem kennis dat dit nie die app toelaat om noodnommers te bel nie. Kwaadwillige apps kan jou geld kos deur oproepe te maak sonder jou bevestiging of diensverskafferkodes te bel wat veroorsaak dat inkomende oproepe outomaties na ’n ander nommer aangestuur word."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"toegang tot kitsboodskapoproepdiens"</string>
- <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die program toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
+ <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die app toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lees foonstatus en identiteit"</string>
- <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
+ <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die app toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die app toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
<string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lees basiese telefoniestatus en -identiteit"</string>
- <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die program toegang tot die toestel se basiese telefoniekenmerke."</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die app toegang tot die toestel se basiese telefoniekenmerke."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"roeteer oproepe deur die stelsel"</string>
- <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
+ <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die app toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sien en beheer oproepe deur die stelsel."</string>
- <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Laat die program toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
+ <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Laat die app toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
<string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"vrygestel van beperkings op oudio-opnames"</string>
- <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Stel die program vry van beperkings om oudio op te neem."</string>
- <string name="permlab_acceptHandover" msgid="2925523073573116523">"gaan voort met \'n oproep uit \'n ander program"</string>
- <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Laat die program toe om \'n oproep voort te sit wat in \'n ander program begin is."</string>
+ <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Stel die app vry van beperkings om oudio op te neem."</string>
+ <string name="permlab_acceptHandover" msgid="2925523073573116523">"gaan voort met \'n oproep uit \'n ander app"</string>
+ <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Laat die app toe om \'n oproep voort te sit wat in \'n ander app begin is."</string>
<string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"lees foonnommers"</string>
- <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Laat die program toe om toegang tot die toestel se foonnommers te kry."</string>
+ <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Laat die app toe om toegang tot die toestel se foonnommers te kry."</string>
<string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"hou motorskerm aan"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"verhoed dat tablet slaap"</string>
<string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"keer dat jou Android TV-toestel slaap"</string>
<string name="permlab_wakeLock" product="default" msgid="569409726861695115">"verhoed foon om te slaap"</string>
- <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Laat die program toe om die motorskerm aan te hou."</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Laat die program toe om die tablet te keer om te slaap."</string>
- <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Laat die program toe om te keer dat jou Android TV-toestel slaap."</string>
- <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Laat die program toe om die foon te keer om te slaap."</string>
+ <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Laat die app toe om die motorskerm aan te hou."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Laat die app toe om die tablet te keer om te slaap."</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Laat die app toe om te keer dat jou Android TV-toestel slaap."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Laat die app toe om die foon te keer om te slaap."</string>
<string name="permlab_transmitIr" msgid="8077196086358004010">"versend infrarooi"</string>
- <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Laat die program toe om die tablet se infrarooisender te gebruik."</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Laat die program toe om jou Android TV-toestel se infrarooisender te gebruik."</string>
- <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Laat die program toe om die foon se infrarooisender te gebruik."</string>
+ <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Laat die app toe om die tablet se infrarooisender te gebruik."</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Laat die app toe om jou Android TV-toestel se infrarooisender te gebruik."</string>
+ <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Laat die app toe om die foon se infrarooisender te gebruik."</string>
<string name="permlab_setWallpaper" msgid="6959514622698794511">"stel muurpapier"</string>
- <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Laat die program toe om die stelsel se muurpapier te stel."</string>
+ <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Laat die app toe om die stelsel se muurpapier te stel."</string>
<string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Kry toegang tot versteekte profiele"</string>
<string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"Laat die app toe om toegang tot versteekte profiele te kry."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"verstel jou muurpapier se grootte"</string>
- <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Laat die program toe om die stelsel se muurpapier se groottewenke te stel."</string>
+ <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Laat die app toe om die stelsel se muurpapier se groottewenke te stel."</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"stel tydsone"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Laat die program toe om die tablet se tydsone te verander."</string>
- <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Laat die program toe om jou Android TV-toestel se tydsone te verander."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Laat die program toe om die foon se tydsone te verander."</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Laat die app toe om die tablet se tydsone te verander."</string>
+ <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Laat die app toe om jou Android TV-toestel se tydsone te verander."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Laat die app toe om die foon se tydsone te verander."</string>
<string name="permlab_getAccounts" msgid="5304317160463582791">"soek rekeninge op die toestel"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Laat die program toe om die lys van rekeninge wat aan die tablet bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur programme wat jy geïnstalleer het, insluit."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Laat die program toe om die lys rekeninge wat aan jou Android TV-toestel bekend is, te kry. Dit kan dalk rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het."</string>
- <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Laat die program toe om die lys van rekeninge wat aan die foon bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur programme wat jy geïnstalleer het, insluit."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Laat die app toe om die lys van rekeninge wat aan die tablet bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur apps wat jy geïnstalleer het, insluit."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Laat die app toe om die lys rekeninge wat aan jou Android TV-toestel bekend is, te kry. Dit kan dalk rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Laat die app toe om die lys van rekeninge wat aan die foon bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur apps wat jy geïnstalleer het, insluit."</string>
<string name="permlab_accessNetworkState" msgid="2349126720783633918">"bekyk netwerkverbindings"</string>
- <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Laat die program toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
+ <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Laat die app toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
<string name="permlab_createNetworkSockets" msgid="3224420491603590541">"verkry volle netwerktoegang"</string>
- <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Laat die program toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander programme verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
+ <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Laat die app toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander apps verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
<string name="permlab_changeNetworkState" msgid="8945711637530425586">"verander netwerkverbinding"</string>
- <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Laat die program toe om die status van netwerkkonnektiwiteit te verander."</string>
+ <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Laat die app toe om die status van netwerkkonnektiwiteit te verander."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"verander verbinde konnektiwiteit"</string>
- <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Laat die program toe om die status van verbinde netwerkkonnektiwiteit te verander."</string>
+ <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Laat die app toe om die status van verbinde netwerkkonnektiwiteit te verander."</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"bekyk Wi-Fi-verbindings"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Laat die program toe om inligting oor Wi-Fi-netwerke, soos of Wi-Fi geaktiveer is en die name van gekoppelde Wi-Fi toestelle, te sien."</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Laat die app toe om inligting oor wi-fi-netwerke, soos of wi-fi geaktiveer is en die name van gekoppelde wi-fi-toestelle, te sien."</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"koppel en ontkoppel van Wi-Fi"</string>
- <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Laat die program toe om te koppel aan en te ontkoppel van Wi-Fi-toegangspunte en om veranderings aan Wi-Fi-netwerke se toestelopstellings te maak."</string>
+ <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Laat die app toe om te koppel aan en te ontkoppel van wi-fi-toegangspunte en om veranderings aan wi-fi-netwerke se toestelopstellings te maak."</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"laat Wi-Fi-multisendontvangs toe"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Laat die program toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou tablet nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Laat die program toe om pakkette te ontvang wat met multi-uitsendingadresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou Android TV-toestel nie. Dit gebruik meer krag as nie-multi-uitsendingmodus."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Laat die program toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou foon nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Laat die app toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou tablet nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Laat die app toe om pakkette te ontvang wat met multi-uitsendingadresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou Android TV-toestel nie. Dit gebruik meer krag as nie-multi-uitsendingmodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Laat die app toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou foon nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"gaan in by Bluetooth-instellings"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Laat die program toe om die plaaslike Bluetooth-tablet op te stel, en om met afstandbeheer toestelle saam te bind."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Laat die program toe om Bluetooth op jou Android TV-toestel op te stel, en om afgeleë toestelle te ontdek en met hulle saam te bind."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Laat die program toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met afgeleë toestelle saam te bind."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Laat die app toe om die plaaslike Bluetooth-tablet op te stel, en om met afstandbeheer toestelle saam te bind."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Laat die app toe om Bluetooth op jou Android TV-toestel op te stel, en om afgeleë toestelle te ontdek en met hulle saam te bind."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Laat die app toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met toestelle op ’n afstand saam te bind."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"koppel aan en ontkoppel van WiMAX"</string>
- <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Laat die program toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
+ <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Laat die app toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"verander WiMAX-status"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Laat die program toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Laat die program toe om jou Android TV-toestel aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Laat die program toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Laat die app toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Laat die app toe om jou Android TV-toestel aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Laat die app toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"bind saam met Bluetooth-toestelle"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Laat die program toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Laat die program toe om die opstelling van Bluetooth op jou Android TV-toestel te bekyk, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Laat die program toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Laat die app toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Laat die app toe om die opstelling van Bluetooth op jou Android TV-toestel te bekyk, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Laat die app toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"ontdek en bind Bluetooth-toestelle in die omtrek saam"</string>
- <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Laat die program toe om Bluetooth-toestelle in die omtrek te ontdek en saam te bind"</string>
+ <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Laat die app toe om Bluetooth-toestelle in die omtrek te ontdek en saam te bind"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"koppel aan saamgebinde Bluetooth-toestelle"</string>
- <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Laat die program toe om aan saamgebinde Bluetooth-toestelle te koppel"</string>
+ <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Laat die app toe om aan saamgebinde Bluetooth-toestelle te koppel"</string>
<string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"adverteer op Bluetooth-toestelle in die omtrek"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Laat die program toe om op Bluetooth-toestelle in die omtrek te adverteer"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Laat die app toe om op Bluetooth-toestelle in die omtrek te adverteer"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"bepaal relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek"</string>
- <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die program toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die app toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"om interaksie met wi‑fi-toestelle in die omtrek te hê"</string>
- <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die program toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
+ <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die app toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
<string name="permlab_ranging" msgid="2854543350668593390">"bepaal relatiewe posisie tussen toestelle in die omtrek"</string>
<string name="permdesc_ranging" msgid="6703905535621521710">"Laat die app toe om relatiewe posisie tussen toestelle in die omtrek te bepaal"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Voorkeur-NFC-betalingdiensinligting"</string>
- <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die program toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
+ <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die app toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"beheer kortveldkommunikasie"</string>
- <string name="permdesc_nfc" msgid="8352737680695296741">"Laat die program toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
+ <string name="permdesc_nfc" msgid="8352737680695296741">"Laat die app toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
<string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"Veilige Element-transaksiegeval"</string>
<string name="permdesc_nfcTransactionEvent" msgid="1904286701876487397">"Laat die app toe om inligting te ontvang oor transaksies wat op ’n Veilige Element plaasvind."</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"deaktiveer jou skermslot"</string>
- <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig."</string>
+ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Laat die app toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en aktiveer dit dan weer wanneer die oproep eindig."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"versoek skermslot-kompleksiteit"</string>
- <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die program dus nie die presiese wagwoord ken nie."</string>
+ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die app toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie app kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die app dus nie die presiese wagwoord ken nie."</string>
<string name="permlab_postNotification" msgid="4875401198597803658">"wys kennisgewings"</string>
- <string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die program toe om kennisgewings te wys"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die app toe om kennisgewings te wys"</string>
<string name="permlab_turnScreenOn" msgid="219344053664171492">"skakel die skerm aan"</string>
- <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Laat die program toe om die skerm aan te skakel."</string>
+ <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Laat die app toe om die skerm aan te skakel."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gebruik biometriese hardeware"</string>
- <string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
+ <string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die app toe om biometriese hardeware vir stawing te gebruik"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"bestuur vingerafdrukhardeware"</string>
- <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Laat die program toe om metodes te benut om vingerafdruksjablone vir gebruik by te voeg en uit te vee."</string>
+ <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Laat die app toe om metodes te benut om vingerafdruktemplate vir gebruik by te voeg en uit te vee."</string>
<string name="permlab_useFingerprint" msgid="1001421069766751922">"gebruik vingerafdrukhardeware"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Laat die program toe om vingerafdrukhardeware vir stawing te gebruik"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Laat die app toe om vingerafdrukhardeware vir stawing te gebruik"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"wysig jou musiekversameling"</string>
- <string name="permdesc_audioWrite" msgid="8057399517013412431">"Laat die program toe om jou musiekversameling te wysig."</string>
+ <string name="permdesc_audioWrite" msgid="8057399517013412431">"Laat die app toe om jou musiekversameling te wysig."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"wysig jou videoversameling"</string>
- <string name="permdesc_videoWrite" msgid="6124731210613317051">"Laat die program toe om jou videoversameling te wysig."</string>
+ <string name="permdesc_videoWrite" msgid="6124731210613317051">"Laat die app toe om jou videoversameling te wysig."</string>
<string name="permlab_imagesWrite" msgid="1774555086984985578">"wysig jou fotoversameling"</string>
- <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Laat die program toe om jou fotoversameling te wysig."</string>
+ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Laat die app toe om jou fotoversameling te wysig."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lees liggings in jou mediaversameling"</string>
- <string name="permdesc_mediaLocation" msgid="597912899423578138">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
+ <string name="permdesc_mediaLocation" msgid="597912899423578138">"Laat die app toe om liggings in jou mediaversameling te lees."</string>
<string name="biometric_app_setting_name" msgid="3339209978734534457">"Gebruik biometrie"</string>
<string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gebruik biometrie of skermslot"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifieer dat dit jy is"</string>
@@ -765,45 +762,45 @@
<string name="face_error_vendor_unknown" msgid="7387005932083302070">"Iets is fout. Probeer weer."</string>
<string name="face_icon_content_description" msgid="465030547475916280">"Gesig-ikoon"</string>
<string name="permlab_readSyncSettings" msgid="6250532864893156277">"lees sinkroniseer-instellings"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Laat die app toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-app met \'n rekening gesinkroniseer is."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"wissel tussen sinkronisasie aan en af"</string>
- <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Laat \'n program toe om die sinkroniseringinstellings van \'n rekening te verander. Byvoorbeeld, dit kan gebruik word om sinkronisasie van die People-program met \'n ander rekening te aktiveer."</string>
+ <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Laat \'n app toe om die sinkroniseringinstellings van \'n rekening te verander. Byvoorbeeld, dit kan gebruik word om sinkronisasie van die People-app met \'n ander rekening te aktiveer."</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"lees sinkroniseerstatistiek"</string>
- <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Laat \'n program toe om die sinkroniseringstatistieke van \'n rekening te lees, insluitend die geskiedenis van sinkroniseringgebeure en hoeveel data gesinkroniseer is."</string>
+ <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Laat \'n app toe om die sinkroniseringstatistieke van \'n rekening te lees, insluitend die geskiedenis van sinkroniseringgebeure en hoeveel data gesinkroniseer is."</string>
<string name="permlab_sdcardRead" msgid="5791467020950064920">"lees jou gedeelde berging se inhoud"</string>
- <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Laat die program toe om jou gedeelde berging se inhoud te lees."</string>
+ <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Laat die app toe om jou gedeelde berging se inhoud te lees."</string>
<string name="permlab_readMediaAudio" msgid="8723513075731763810">"lees oudiolêers in gedeelde berging"</string>
- <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Laat die program toe om oudiolêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Laat die app toe om oudiolêers in jou gedeelde berging te lees."</string>
<string name="permlab_readMediaVideo" msgid="7768003311260655007">"lees videolêers in gedeelde berging"</string>
- <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Laat die program toe om videolêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Laat die app toe om videolêers in jou gedeelde berging te lees."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lees prentlêers in gedeelde berging"</string>
- <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Laat die program toe om prentlêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Laat die app toe om prentlêers in jou gedeelde berging te lees."</string>
<string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"lees prent- en videolêers wat gebruiker in gedeelde berging kies"</string>
<string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Laat die app toe om prent- en videolêers te lees wat jy in jou gedeelde berging kies."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"verander of vee jou gedeelde berging se inhoud uit"</string>
- <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die program toe om jou gedeelde berging se inhoud te skryf."</string>
+ <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die app toe om jou gedeelde berging se inhoud te skryf."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"maak en/of ontvang SIP-oproepe"</string>
- <string name="permdesc_use_sip" msgid="3590270893253204451">"Laat die program toe om SIP-oproepe te maak en te ontvang."</string>
+ <string name="permdesc_use_sip" msgid="3590270893253204451">"Laat die app toe om SIP-oproepe te maak en te ontvang."</string>
<string name="permlab_register_sim_subscription" msgid="1653054249287576161">"registreer nuwe telekommunikasie-SIM-verbindings"</string>
- <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Laat die program toe om nuwe telekommunikasie-SIM-verbindings te registreer."</string>
+ <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Laat die app toe om nuwe telekommunikasie-SIM-verbindings te registreer."</string>
<string name="permlab_register_call_provider" msgid="6135073566140050702">"registreer nuwe telekommunikasieverbindings"</string>
- <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Laat die program toe om nuwe telekommunikasieverbindings te registreer."</string>
+ <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Laat die app toe om nuwe telekommunikasieverbindings te registreer."</string>
<string name="permlab_connection_manager" msgid="3179365584691166915">"bestuur telekom-verbindings"</string>
- <string name="permdesc_connection_manager" msgid="1426093604238937733">"Laat die program toe om telekom-verbindings te bestuur."</string>
+ <string name="permdesc_connection_manager" msgid="1426093604238937733">"Laat die app toe om telekom-verbindings te bestuur."</string>
<string name="permlab_bind_incall_service" msgid="5990625112603493016">"beleef interaksie met inoproep-skerm"</string>
- <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Laat die program beheer wanneer en hoe die gebruiker die inoproep-skerm sien."</string>
+ <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Laat die app beheer wanneer en hoe die gebruiker die inoproep-skerm sien."</string>
<string name="permlab_bind_connection_service" msgid="5409268245525024736">"werk met telefoniedienste saam"</string>
- <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Laat die program toe om met telefoniedienste saam te werk om oproepe te maak of ontvang."</string>
+ <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Laat die app toe om met telefoniedienste saam te werk om oproepe te maak of ontvang."</string>
<string name="permlab_control_incall_experience" msgid="6436863486094352987">"bied \'n inoproep-gebruikerervaring"</string>
- <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Laat die program toe om \'n inoproep-gebruikerervaring te bied."</string>
+ <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Laat die app toe om \'n inoproep-gebruikerervaring te bied."</string>
<string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"lees netwerkgebruik-geskiedenis"</string>
- <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Laat die program toe om historiese netwerkgebruik vir spesifieke netwerke en programme te lees."</string>
+ <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Laat die app toe om historiese netwerkgebruik vir spesifieke netwerke en apps te lees."</string>
<string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"bestuur netwerkbeleid"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Laat die program toe om netwerkbeleide te bestuur en program-spesifieke reëls te definieer."</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Laat die app toe om netwerkbeleide te bestuur en app-spesifieke reëls te definieer."</string>
<string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"verander verrekening van netwerkgebruik"</string>
- <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string>
+ <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Laat die app toe om te verander hoe netwerkgebruik teenoor apps gemeet word. Nie vir gebruik deur normale apps nie."</string>
<string name="permlab_accessNotifications" msgid="7130360248191984741">"kry toegang tot kennisgewings"</string>
- <string name="permdesc_accessNotifications" msgid="761730149268789668">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
+ <string name="permdesc_accessNotifications" msgid="761730149268789668">"Laat die app toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander apps geplaas is."</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"bind aan \'n kennisgewingluisteraardiens"</string>
<string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"verbind met \'n toestandverskafferdiens"</string>
@@ -813,29 +810,29 @@
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"roep die opstellingprogram op wat deur die diensverskaffer voorsien is"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Laat die houer toe om die opstellingsprogram wat deur die diensverskaffer voorsien word, op te roep. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"luister vir waarnemings oor netwerktoestande"</string>
- <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Laat \'n program luister vir waarnemings oor netwerktoestande. Behoort nooit nodig te wees vir normale programme nie."</string>
+ <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Laat \'n app luister vir waarnemings oor netwerktoestande. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"verander invoertoestelkalibrasie"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Laat die program toe om die kalibrasieparameters van die raakskerm te wysig. Dit behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Laat die app toe om die kalibrasieparameters van die raakskerm te wysig. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"gaan in by DRM-sertifikate"</string>
- <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Laat \'n program toe om DRM-sertifikate op te stel en te gebruik. Behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Laat \'n app toe om DRM-sertifikate op te stel en te gebruik. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"ontvang Android Straal-oordragstatus"</string>
- <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Laat hierdie program toe om inligting oor huidige Android Straal-oordragte te ontvang."</string>
+ <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Laat hierdie app toe om inligting oor huidige Android Straal-oordragte te ontvang."</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"verwyder DRM-sertifikate"</string>
- <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Laat \'n program toe om DRM-sertifikate te verwyder. Behoort nooit vir gewone programme nodig te wees nie."</string>
+ <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Laat \'n app toe om DRM-sertifikate te verwyder. Dit behoort nooit vir gewone apps nodig te wees nie."</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"bind aan \'n diensverskaffer-boodskapdiens"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Dit laat die houer toe om aan die top-koppelvlak van \'n diensverskaffer-boodskapdiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"verbind aan diensverskafferdienste"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Laat die houer toe om aan diensverskafferdienste te verbind. Behoort nooit vir normale programme nodig te wees nie."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"verkry toegang tot Moenie Steur Nie"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die app toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
- <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n app te begin. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"begin Bekyk Toestemmingbesluite"</string>
<string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Laat die houer toe om skerm te begin om toestemmingbesluite na te gaan. Behoort nooit vir normale programme nodig te wees nie."</string>
<string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"begin Bekyk Programkenmerke"</string>
- <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n program te begin bekyk."</string>
+ <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n app te begin bekyk."</string>
<string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string>
- <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die program toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die app toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
<string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"dateer app sonder gebruikerhandeling op"</string>
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Laat die houer toe om die app wat dit voorheen sonder gebruikhandeling geïnstalleer het, op te dateer"</string>
<string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"dateer die verifikasiestatus van E2EE-kontaksleutels op wat deur ander apps besit word"</string>
@@ -989,7 +986,7 @@
<string name="sipAddressTypeHome" msgid="5918441930656878367">"Tuis"</string>
<string name="sipAddressTypeWork" msgid="7873967986701216770">"Werk"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"Ander"</string>
- <string name="quick_contacts_not_available" msgid="1262709196045052223">"Geen program gekry om hierdie kontak te bekyk nie."</string>
+ <string name="quick_contacts_not_available" msgid="1262709196045052223">"Geen app gekry om hierdie kontak te bekyk nie."</string>
<string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Voer PIN-kode in"</string>
<string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Voer PUK en nuwe PIN-kode in"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK-kode"</string>
@@ -1107,9 +1104,9 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nIs jy seker dat jy weg van hierdie bladsy af wil navigeer?"</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Outovul met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"stel \'n wekker"</string>
- <string name="permdesc_setAlarm" msgid="2185033720060109640">"Laat die program toe om \'n alarm in \'n geïnstalleerde wekkerprogram te stel. Sommige wekkerprogramme werk dalk nie met hierdie funksie nie."</string>
+ <string name="permdesc_setAlarm" msgid="2185033720060109640">"Laat die app toe om \'n alarm in \'n geïnstalleerde wekkerapp te stel. Sommige wekkerapps werk dalk nie met hierdie funksie nie."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"voeg stemboodskap by"</string>
- <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Laat die program toe om boodskappe by te voeg by jou stempos-inkassie."</string>
+ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Laat die app toe om boodskappe by te voeg by jou stempos-inkassie."</string>
<string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het van jou knipbord af geplak"</string>
<string name="more_item_label" msgid="7419249600215749115">"Meer"</string>
<string name="prepend_shortcut_label" msgid="1743716737502867951">"Kieslys+"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"oor <xliff:g id="COUNT">%d</xliff:g> u."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"oor <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"oor <xliff:g id="COUNT">%d</xliff:g> j."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> m. gelede"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> uur gelede"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d. gelede"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> jr. gelede"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> uur"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> jr."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"oor <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"oor <xliff:g id="COUNT">%d</xliff:g> uur"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"oor <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"oor <xliff:g id="COUNT">%d</xliff:g> jr."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. gelede"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> uur gelede"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d. gelede"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> jr. gelede"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut gelede}other{# minute gelede}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur gelede}other{# uur gelede}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag gelede}other{# dae gelede}}"</string>
@@ -1208,7 +1221,7 @@
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
- <string name="app_running_notification_text" msgid="5120815883400228566">"Tik vir meer inligting of om die program te stop."</string>
+ <string name="app_running_notification_text" msgid="5120815883400228566">"Tik vir meer inligting of om die app te stop."</string>
<string name="ok" msgid="2646370155170753815">"OK"</string>
<string name="cancel" msgid="6908697720451760115">"Kanselleer"</string>
<string name="yes" msgid="9069828999585032361">"OK"</string>
@@ -1243,28 +1256,28 @@
<string name="whichSendToApplication" msgid="77101541959464018">"Stuur met"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Stuur met %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Stuur"</string>
- <string name="whichHomeApplication" msgid="8276350727038396616">"Kies \'n Tuis-program"</string>
+ <string name="whichHomeApplication" msgid="8276350727038396616">"Kies \'n Tuis-app"</string>
<string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Gebruik %1$s as Tuis"</string>
<string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Vang prent vas"</string>
<string name="whichImageCaptureApplication" msgid="2737413019463215284">"Vang prent vas met"</string>
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Vang prent vas met %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Vang prent vas"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Gebruik hierdie aksie by verstek."</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"Gebruik \'n ander program"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"Gebruik \'n ander app"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"Vee die verstek instelling uit in Stelselinstellings &gt; Programme &gt; Afgelaai."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Kies \'n handeling"</string>
- <string name="chooseUsbActivity" msgid="2096269989990986612">"Kies \'n program vir die USB-toestel"</string>
+ <string name="chooseUsbActivity" msgid="2096269989990986612">"Kies \'n app vir die USB-toestel"</string>
<string name="noApplications" msgid="1186909265235544019">"Geen programme kan hierdie handeling uitvoer nie."</string>
<string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> het gestop"</string>
<string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> het gestop"</string>
<string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> stop aanhoudend"</string>
<string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> stop aanhoudend"</string>
- <string name="aerr_restart" msgid="2789618625210505419">"Maak program weer oop"</string>
+ <string name="aerr_restart" msgid="2789618625210505419">"Maak app weer oop"</string>
<string name="aerr_report" msgid="3095644466849299308">"Stuur terugvoer"</string>
<string name="aerr_close" msgid="3398336821267021852">"Maak toe"</string>
<string name="aerr_mute" msgid="2304972923480211376">"Demp totdat toestel herbegin"</string>
<string name="aerr_wait" msgid="3198677780474548217">"Wag"</string>
- <string name="aerr_close_app" msgid="8318883106083050970">"Maak program toe"</string>
+ <string name="aerr_close_app" msgid="8318883106083050970">"Maak app toe"</string>
<string name="anr_title" msgid="7290329487067300120"></string>
<string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> reageer nie"</string>
<string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> reageer nie"</string>
@@ -1274,7 +1287,7 @@
<string name="report" msgid="2149194372340349521">"Verslag"</string>
<string name="wait" msgid="7765985809494033348">"Wag"</string>
<string name="webpage_unresponsive" msgid="7850879412195273433">"Die bladsy reageer nie meer nie.\n\nWil jy dit toemaak?"</string>
- <string name="launch_warning_title" msgid="6725456009564953595">"Program herlei"</string>
+ <string name="launch_warning_title" msgid="6725456009564953595">"App is herlei"</string>
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop nou."</string>
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> is oorspronklik laat loop."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Skaal"</string>
@@ -1282,10 +1295,10 @@
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"Heraktiveer hierdie in Stelselinstellings &gt; Programme &gt; Afgelaai."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> steun nie die huidige skermgrootte-instelling nie en sal dalk onverwags reageer."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Wys altyd"</string>
- <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gebou vir \'n onversoenbare weergawe van die Android-bedryfstelsel en kan dalk op \'n onverwagte manier reageer. \'n Opgedateerde weergawe van die program is dalk beskikbaar."</string>
+ <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gebou vir \'n onversoenbare weergawe van die Android-bedryfstelsel en kan dalk op \'n onverwagte manier reageer. \'n Opgedateerde weergawe van die app is dalk beskikbaar."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Wys altyd"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Kyk vir opdatering"</string>
- <string name="smv_application" msgid="3775183542777792638">"Die program <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
+ <string name="smv_application" msgid="3775183542777792638">"Die app <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
<string name="smv_process" msgid="1398801497130695446">"Die proses <xliff:g id="PROCESS">%1$s</xliff:g> het die selfopgelegde StrictMode-beleid geskend."</string>
<string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Foon dateer tans op …"</string>
<string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"Tablet dateer tans oop …"</string>
@@ -1317,7 +1330,7 @@
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g>-hoopstorting is gereed"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"Hoopstorting is ingesamel. Tik om te deel."</string>
<string name="dump_heap_title" msgid="4367128917229233901">"Deel hoopstorting?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy met sy ontwikkelaar kan deel. Pas op: Hierdie hoopstorting bevat dalk van jou persoonlike inligting waartoe die program toegang het."</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy met sy ontwikkelaar kan deel. Pas op: Hierdie hoopstorting bevat dalk van jou persoonlike inligting waartoe die app toegang het."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy kan deel. Wees versigtig: Hierdie hoopstorting kan enige sensitiewe persoonlike inligting bevat waarby die proses kan ingaan, wat goed kan insluit wat jy getik het."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"\'n Hoopstorting van <xliff:g id="PROC">%1$s</xliff:g> se proses is beskikbaar vir jou om te deel. Wees versigtig: Hierdie hoopstorting kan sensitiewe persoonlike inligting bevat waarby die proses kan ingaan, wat goed kan insluit wat jy getik het."</string>
<string name="sendText" msgid="493003724401350724">"Kies \'n handeling vir teks"</string>
@@ -1368,7 +1381,7 @@
<string name="decline" msgid="6490507610282145874">"Weier"</string>
<string name="select_character" msgid="3352797107930786979">"Voeg karakter in"</string>
<string name="sms_control_title" msgid="4748684259903148341">"Stuur SMS-boodskappe"</string>
- <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie program toelaat om voort te gaan om boodskappe te stuur?"</string>
+ <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie app toelaat om voort te gaan om boodskappe te stuur?"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"Laat toe"</string>
<string name="sms_control_no" msgid="4845717880040355570">"Weier"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; wil \'n boodskap na &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; stuur."</string>
@@ -1388,8 +1401,8 @@
<string name="sim_restart_button" msgid="8481803851341190038">"Herbegin"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Aktiveer mobiele diens"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Laai die diensverskafferprogram af om jou nuwe SIM te aktiveer"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Laai die <xliff:g id="APP_NAME">%1$s</xliff:g>-program af om jou nuwe SIM te aktiveer"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Laai program af"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Laai die <xliff:g id="APP_NAME">%1$s</xliff:g>-app af om jou nuwe SIM te aktiveer"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Laai app af"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Nuwe SIM is ingesit"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Tik om dit op te stel"</string>
<string name="time_picker_dialog_title" msgid="9053376764985220821">"Stel tyd"</string>
@@ -1502,17 +1515,17 @@
<string name="ext_media_status_missing" msgid="6520746443048867314">"Nie ingevoeg nie"</string>
<string name="activity_list_empty" msgid="4219430010716034252">"Geen passende aktiwiteite gevind nie."</string>
<string name="permlab_route_media_output" msgid="8048124531439513118">"roeteer media-uitvoer"</string>
- <string name="permdesc_route_media_output" msgid="1759683269387729675">"Laat \'n program toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
+ <string name="permdesc_route_media_output" msgid="1759683269387729675">"Laat \'n app toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
<string name="permlab_readInstallSessions" msgid="7279049337895583621">"lees installeersessies"</string>
- <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Laat \'n program toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
+ <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Laat \'n app toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
<string name="permlab_requestInstallPackages" msgid="7600020863445351154">"versoek installeerpakkette"</string>
- <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Laat \'n program toe om te versoek dat pakkette geïnstalleer word."</string>
+ <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Laat \'n app toe om te versoek dat pakkette geïnstalleer word."</string>
<string name="permlab_requestDeletePackages" msgid="2541172829260106795">"versoek uitvee van pakkette"</string>
- <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n program toe om te versoek dat pakkette uitgevee word."</string>
+ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n app toe om te versoek dat pakkette uitgevee word."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vra om batteryoptimerings te ignoreer"</string>
- <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string>
+ <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n app toe om toestemming te vra om batteryoptimerings vir daardie app te ignoreer."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
- <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n app toe om alle geïnstalleerde pakette te sien."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string>
@@ -1532,8 +1545,8 @@
<string name="permission_request_notification_title" msgid="1810025922441048273">"Toestemming versoek"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Toestemming versoek\nvir rekening <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
<string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Toestemming versoek deur <xliff:g id="APP">%1$s</xliff:g>\nvir rekening <xliff:g id="ACCOUNT">%2$s</xliff:g>"</string>
- <string name="forward_intent_to_owner" msgid="4620359037192871015">"Jy gebruik hierdie program buite jou werkprofiel"</string>
- <string name="forward_intent_to_work" msgid="3620262405636021151">"Jy gebruik tans hierdie program in jou werkprofiel"</string>
+ <string name="forward_intent_to_owner" msgid="4620359037192871015">"Jy gebruik hierdie app buite jou werkprofiel"</string>
+ <string name="forward_intent_to_work" msgid="3620262405636021151">"Jy gebruik tans hierdie app in jou werkprofiel"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"Invoermetode"</string>
<string name="sync_binding_label" msgid="469249309424662147">"Sinkroniseer"</string>
<string name="accessibility_binding_label" msgid="1974602776545801715">"Toeganklikheid"</string>
@@ -1609,7 +1622,7 @@
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus verander"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="168054869339091055">"Invoersleutel"</string>
- <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Kies \'n program"</string>
+ <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Kies \'n app"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Kon <xliff:g id="APPLICATION_NAME">%s</xliff:g> nie begin nie"</string>
<string name="shareactionprovider_share_with" msgid="2753089758467748982">"Deel met"</string>
<string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Deel met <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
@@ -1782,7 +1795,7 @@
<string name="guest_name" msgid="8502103277839834324">"Gas"</string>
<string name="error_message_title" msgid="4082495589294631966">"Fout"</string>
<string name="error_message_change_not_allowed" msgid="843159705042381454">"Jou administrateur laat nie hierdie verandering toe nie"</string>
- <string name="app_not_found" msgid="3429506115332341800">"Geen program gevind om hierdie handeling te hanteer nie"</string>
+ <string name="app_not_found" msgid="3429506115332341800">"Geen app gevind om hierdie handeling te hanteer nie"</string>
<string name="revoke" msgid="5526857743819590458">"Herroep"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
<string name="mediasize_iso_a1" msgid="4063589931031977223">"ISO A1"</string>
@@ -2011,10 +2024,10 @@
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle tale"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Allle streke"</string>
<string name="locale_search_menu" msgid="6258090710176422934">"Soek"</string>
- <string name="app_suspended_title" msgid="888873445010322650">"Program is nie beskikbaar nie"</string>
+ <string name="app_suspended_title" msgid="888873445010322650">"App is nie beskikbaar nie"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is nie nou onmiddellik beskikbaar nie. Dit word bestuur deur <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Kom meer te wete"</string>
- <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Hervat program"</string>
+ <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Hervat app"</string>
<string name="work_mode_off_title" msgid="6367463960165135829">"Hervat werkapps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Hervat"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
@@ -2022,7 +2035,7 @@
<string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stel skermslot"</string>
<string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stel ’n skermslot op dié toestel om jou privaat ruimte te gebruik"</string>
<string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Stel ’n skermslot op dié toestel om privaat ruimte uit te vee"</string>
- <string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
+ <string name="app_blocked_title" msgid="7353262160455028160">"App is nie beskikbaar nie"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> is nie beskikbaar nie"</string>
<string name="app_streaming_blocked_title_for_permission_dialog" msgid="3805704317624448487">"Toestemmingsversoek is onderdruk"</string>
@@ -2039,9 +2052,9 @@
<string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou Android TV-toestel."</string>
<string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou tablet."</string>
<string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou foon."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou Android TV-toestel."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou tablet."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou foon."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou Android TV-toestel."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou tablet."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou foon."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou Android TV-toestel."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou foon."</string>
@@ -2049,7 +2062,7 @@
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kyk vir opdatering"</string>
<string name="deprecated_abi_message" msgid="6820548011196218091">"Hierdie app is nie met die jongste weergawe van Android versoenbaar nie. Kyk of daar ’n opdatering is, of kontak die app se ontwikkelaar."</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Jy het nuwe boodskappe"</string>
- <string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-program oop om te bekyk"</string>
+ <string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-app oop om te bekyk"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"Sommige funksies kan beperk wees"</string>
<string name="profile_encrypted_detail" msgid="5279730442756849055">"Werkprofiel is gesluit"</string>
<string name="profile_encrypted_message" msgid="1128512616293157802">"Tik om werkprofiel te ontsluit"</string>
@@ -2128,13 +2141,13 @@
<string name="popup_window_default_title" msgid="6907717596694826919">"Opspringvenster"</string>
<string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Programweergawe is afgegradeer, of is nie met hierdie kortpad versoenbaar nie"</string>
- <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Kon nie die kortpad teruglaai nie omdat die program nie rugsteun en teruglaai steun nie"</string>
+ <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Kon nie die kortpad teruglaai nie omdat die app nie rugsteun en teruglaai steun nie"</string>
<string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Kon nie teruglaai nie omdat programondertekening nie ooreenstem nie"</string>
<string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Kon nie kortpad teruglaai nie"</string>
<string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Kortpad is gedeaktiveer"</string>
<string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEΪNSTALLEER"</string>
<string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"MAAK TOG OOP"</string>
- <string name="harmful_app_warning_title" msgid="8794823880881113856">"Skadelike program is bespeur"</string>
+ <string name="harmful_app_warning_title" msgid="8794823880881113856">"Skadelike app is bespeur"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil <xliff:g id="APP_2">%2$s</xliff:g>-skyfies wys"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Wysig"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Oproepe en kennisgewings sal vibreer"</string>
@@ -2171,7 +2184,7 @@
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Tablet se battery het genoeg krag. Kenmerke is nie meer beperk nie."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Toestel se battery het genoeg krag. Kenmerke is nie meer beperk nie."</string>
<string name="mime_type_folder" msgid="2203536499348787650">"Vouer"</string>
- <string name="mime_type_apk" msgid="3168784749499623902">"Android-program"</string>
+ <string name="mime_type_apk" msgid="3168784749499623902">"Android-app"</string>
<string name="mime_type_generic" msgid="4606589110116560228">"Lêer"</string>
<string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g>-lêer"</string>
<string name="mime_type_audio" msgid="4933450584432509875">"Oudio"</string>
@@ -2193,7 +2206,7 @@
<string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # lêer}other{{file_name} + # lêers}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Geen mense om mee te deel is aanbeveel nie"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Programmelys"</string>
- <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Opneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem."</string>
+ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Opneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Tuis"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Terug"</string>
<string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Onlangse programme"</string>
@@ -2374,8 +2387,8 @@
<string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> is vertaal."</string>
<string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Boodskap is vertaal uit <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> in <xliff:g id="TO_LANGUAGE">%2$s</xliff:g>."</string>
<string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Agtergrondaktiwiteit"</string>
- <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"’n Program maak tans die battery pap"</string>
- <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"’n Program is nog aktief"</string>
+ <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"’n App maak tans die battery pap"</string>
+ <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"’n App is nog aktief"</string>
<string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> loop tans op die agtergrond. Tik om batterygebruik te bestuur."</string>
<string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> kan batterylewe beïnvloed. Tik om aktiewe programme na te gaan."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Gaan aktiewe programme na"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index dcb7186e6f5c..ecfce08a6ae8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"የደዋይ ID ወደ ተከልክሏል ነባሪዎች።ቀጥሎ ጥሪ፡ አልተከለከለም"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"የደዋይ ID ወደ አልተከለከለም ነባሪዎች።ቀጥሎ ጥሪ፡ ተከልክሏል"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"የደዋይ ID ነባሪዎች ወደአልተከለከለም። ቀጥሎ ጥሪ፡አልተከለከለም"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። የኤፒኬ አሰላለፍ ፍተሻ አልተሳካም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; የሚለውን ይመልከቱ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። የELF አሰላለፍ ፍተሻ አልተሳካም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; የሚለውን ይመልከቱ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። ኤፒኬ እና ELF አሰላለፍ ፍተሻዎች አልተሳኩም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; የሚለውን ይመልከቱ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"አገልግሎት አልቀረበም።"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"የደዋይ መታወቂያ ቅንብሮች መለወጥ አትችልም፡፡"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ውሂብ ወደ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ተቀይሯል"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"በ<xliff:g id="COUNT">%d</xliff:g> ሰ ውስጥ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"በ<xliff:g id="COUNT">%d</xliff:g> ቀ ውስጥ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"በ<xliff:g id="COUNT">%d</xliff:g> ዓ ውስጥ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"ከ<xliff:g id="COUNT">%d</xliff:g>ደ በፊት"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"ከ<xliff:g id="COUNT">%d</xliff:g>ሰዓ በፊት"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"ከ<xliff:g id="COUNT">%d</xliff:g>ቀ በፊት"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"ከ<xliff:g id="COUNT">%d</xliff:g>ዓ በፊት"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>ደቂቃ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ሰዓ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ቀ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>ዓመ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"በ<xliff:g id="COUNT">%d</xliff:g>ደቂቃ ውስጥ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"በ<xliff:g id="COUNT">%d</xliff:g>ሰዓ ውስጥ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"በ<xliff:g id="COUNT">%d</xliff:g>ቀ ውስጥ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"በ<xliff:g id="COUNT">%d</xliff:g>ዓመ ውስጥ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"ከ<xliff:g id="COUNT">%d</xliff:g>ደቂቃ በፊት"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"ከ<xliff:g id="COUNT">%d</xliff:g>ሰዓ በፊት"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"ከ<xliff:g id="COUNT">%d</xliff:g>ቀ በፊት"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"ከ<xliff:g id="COUNT">%d</xliff:g>ዓመ በፊት"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ከ# ደቂቃ በፊት}one{# ደቂቃ በፊት}other{# ደቂቃዎች በፊት}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ከ# ሰዓት በፊት}one{ከ# ሰዓት በፊት}other{ከ# ሰዓታት በፊት}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ከ# ቀን በፊት}one{ከ# ቀን በፊት}other{ከ# ቀናት በፊት}}"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 9c590ad25bbc..dc047acfc03b 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1162,6 +1162,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"خلال <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"خلال <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"خلال <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"قبل <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"قبل <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"قبل <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"قبل <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"خلال <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"خلال <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"خلال <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"خلال <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"قبل <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"قبل <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"قبل <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"قبل <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{قبل دقيقة واحدة}zero{قبل # دقيقة}two{قبل دقيقتين}few{قبل # دقائق}many{قبل # دقيقة}other{قبل # دقيقة}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{قبل ساعة واحدة}zero{قبل # ساعة}two{قبل ساعتين}few{قبل # ساعات}many{قبل # ساعة}other{قبل # ساعة}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{قبل يوم واحد}zero{قبل # يوم}two{قبل يومين}few{قبل # أيام}many{قبل # يومًا}other{قبل # يوم}}"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 319cf4bd316b..402394d07770 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"কলাৰ আইডি সীমিত কৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হৈছে"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"এই এপ্‌টো ১৬ কেবিৰ সমিল নহয়। APKৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্‌টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;লৈ যাওক"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"এই এপ্‌টো ১৬ কেবিৰ সমিল নহয়। ELFৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্‌টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;লৈ যাওক"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"এই এপ্‌টো ১৬ কেবিৰ সমিল নহয়। APK আৰু ELFৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্‌টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;লৈ যাওক"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ডেটা <xliff:g id="CARRIERDISPLAY">%s</xliff:g>লৈ সলনি কৰা হৈছে"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টাত"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনত"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> বছৰত"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> মিনিট পূৰ্বে"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা পূৰ্বে"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> দিন পূৰ্বে"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> বছৰৰ পূৰ্বে"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> মিনিট"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> দিন"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> বছৰ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> মিনিটত"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টাত"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> দিনত"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> বছৰত"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> মিনিট পূৰ্বে"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা পূৰ্বে"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> দিন পূৰ্বে"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> বছৰৰ পূৰ্বে"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট পূৰ্বে}one{# মিনিট পূৰ্বে}other{# মিনিট পূৰ্বে}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা পূৰ্বে}one{# ঘণ্টা পূৰ্বে}other{# ঘণ্টা পূৰ্বে}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন পূর্বে}one{# দিন পূৰ্বে}other{# দিন পূৰ্বে}}"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 1f8fe2c70d24..c696e63ed1af 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağandır"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu tətbiq 16 KB ilə uyğunlaşmır. APK düzləndirmə yoxlaması alınmadı. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu tətbiq 16 KB ilə uyğunlaşmır. ELF düzləndirmə yoxlaması alınmadı. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu tətbiq 16 KB ilə uyğunlaşmır. APK və ELF düzləndirmə yoxlamaları uğursuz oldu. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xidmət təmin edilməyib."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Çağrı kimliyi ayarını dəyişə bilməzsiniz."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoruna keçirilib"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> s ərzində"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> g ərzində"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> il ərzində"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə əvvəl"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> saat əvvəl"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> gün əvvəl"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> il əvvəl"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> saat"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> gün"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> il"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə ərzində"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> saat ərzində"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> gün ərzində"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> il ərzində"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə əvvəl"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> saat əvvəl"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> gün əvvəl"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> il əvvəl"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dəqiqə əvvəl}other{# dəqiqə əvvəl}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat əvvəl}other{# saat əvvəl}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün əvvəl}other{# gün əvvəl}}"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 645239d5d9c3..dc393463af87 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID pozivaoca je podrazumevano ograničen. Sledeći poziv: Nije ograničen."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: ograničen."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: Nije ograničen."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provera usklađenosti APK-a nije uspela. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provera usklađenosti ELF-a nije uspela. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provere usklađenosti APK-a i ELF-a nisu uspele. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije dobavljena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete da promenite podešavanje ID-a korisnika."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobilni podaci su prebačeni na operatera <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pre <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pre <xliff:g id="COUNT">%d</xliff:g> s"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"pre <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"pre <xliff:g id="COUNT">%d</xliff:g> g"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> s"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pre <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"pre <xliff:g id="COUNT">%d</xliff:g> s"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"pre <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"pre <xliff:g id="COUNT">%d</xliff:g> god"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pre # minut}one{Pre # minut}few{Pre # minuta}other{Pre # minuta}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pre # sat}one{Pre # sat}few{Pre # sata}other{Pre # sati}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pre # dan}one{Pre # dan}few{Pre # dana}other{Pre # dana}}"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 7fce2985f796..d1a2104dfa25 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ідэнтыфікатар АВН па змаўчанні абмежаваны. Наступны выклік: не абмежавана"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Па змаўчанні ідэнтыфікатар АВН не абмежаваны. Наступны выклік: абмежаваны"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Налады ідэнтыфікатару АВН па змаўчанні: не абмяжавана. Наступны выклік: не абмежавана"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне APK. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне ELF. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне APK і ELF. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Служба не прадастаўляецца."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Вы не можаце змяніць налады ідэнтыфікатара абанента, якi тэлефануе."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мабільны трафік пераключаны на аператара \"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>\""</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"праз <xliff:g id="COUNT">%d</xliff:g> гадз"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"праз <xliff:g id="COUNT">%d</xliff:g> сут"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"праз <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> хв таму"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> гадз таму"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> сут таму"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> г. таму"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> хв"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> гадз"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> сут"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"праз <xliff:g id="COUNT">%d</xliff:g> хв"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"праз <xliff:g id="COUNT">%d</xliff:g> гадз"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"праз <xliff:g id="COUNT">%d</xliff:g> сут"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"праз <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> хв таму"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> гадз таму"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> сут таму"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> г. таму"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвіліну таму}one{# хвіліну таму}few{# хвіліны таму}many{# хвілін таму}other{# хвіліны таму}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# гадзіну таму}one{# гадзіну таму}few{# гадзіны таму}many{# гадзін таму}other{# гадзіны таму}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# дзень таму}one{# дзень таму}few{# дні таму}many{# дзён таму}other{# дня таму}}"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index aa03a69e9db3..3332195635c4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Стандартната идентификация на повикванията е „забранено“. За следващото обаждане тя е разрешена."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е забранена."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е разрешена."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Това приложение не е съвместимо със страници с размер 16 KB. Проверката за съответствие с APK файла не бе успешна. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Това приложение не е съвместимо със страници с размер 16 KB. Проверката за съответствие с ELF файла не бе успешна. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Това приложение не е съвместимо със страници с размер 16 KB. Проверките за съответствие с APK и ELF файловете не бяха успешни. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е обезпечена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да променяте настройката за идентификация на обажданията."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Преминахте към мобилни данни от <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"след <xliff:g id="COUNT">%d</xliff:g> ч"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"след <xliff:g id="COUNT">%d</xliff:g> д"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"след <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"преди <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"преди <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"преди <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"преди <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"след <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"след <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"след <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"след <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"преди <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"преди <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"преди <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"преди <xliff:g id="COUNT">%d</xliff:g> г."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{преди # минута}other{преди # минути}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{преди # час}other{преди # часа}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{преди # ден}other{преди # дни}}"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 84d9c20b6d40..2b19c573e04a 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। APK অ্যালাইনমেন্ট চেক করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; দেখুন"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। ELF অ্যালাইনমেন্ট চেক করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; দেখুন"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। APK ও ELF অ্যালাইনমেন্ট পরীক্ষা করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; দেখুন"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"পরিষেবা প্রস্তুত নয়৷"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপনি কলার আইডি এর সেটিংস পরিবর্তন করতে পারবেন না৷"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>-এর ডেটা ব্যবহার করা হয়েছে"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টার মধ্যে"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনের মধ্যে"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>বছরের মধ্যে"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>মিনিট আগে"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টা আগে"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>দিন আগে"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>বছর আগে"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>মিনিট"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টা"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>দিন"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>বছর"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>মিনিটের মধ্যে"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টার মধ্যে"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>দিনের মধ্যে"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>বছরের মধ্যে"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>মিনিট আগে"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>ঘন্টা আগে"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>দিন আগে"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>বছর আগে"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট আগে}one{# মিনিট আগে}other{# মিনিট আগে}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা আগে}one{# ঘণ্টা আগে}other{# ঘণ্টা আগে}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন আগে}one{# দিন আগে}other{# দিন আগে}}"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 13d4ac306cff..c49e337d6fe5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Prikaz ID-a pozivaoca u zadanim postavkama zabranjen. Sljedeći poziv: nije zabranjen"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: zabranjen"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: nije zabranjen"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjera poravnanja APK-a nije uspjela. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjera poravnanja ELF-a nije uspjela. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjere poravnanja APK-a i ELF-a nisu uspjele. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Uslugu nije moguće koristiti."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavke ID-a pozivaoca."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Prijenos podataka usmjeravanjem na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"prije <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"prije <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a5287c6b651c..3bb58a59945a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"El valor predeterminat de l\'identificador de trucada és restringit. Trucada següent: no restringit"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: restringit"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: no restringit"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'APK. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'ELF. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'APK i ELF. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"No s\'ha proveït el servei."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No pots canviar la configuració de l\'identificador de trucada."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Les dades s\'han canviat a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"fa <xliff:g id="COUNT">%d</xliff:g> m"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"fa <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"fa <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"fa <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> hora"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> dia"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> any"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> hora"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> dia"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> any"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"fa <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"fa <xliff:g id="COUNT">%d</xliff:g> hora"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"fa <xliff:g id="COUNT">%d</xliff:g> dia"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"fa <xliff:g id="COUNT">%d</xliff:g> any"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fa # minut}many{Fa # minuts}other{Fa # minuts}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fa # hora}many{Fa # hores}other{Fa # hores}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fa # dia}many{Fa # dies}other{Fa # dies}}"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ad4df0fde106..8038a296ab7a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ve výchozím nastavení je funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Omezeno"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru APK selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru ELF selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tato aplikace není kompatibilní s 16KB režimem. Kontroly zarovnání souborů APK a ELF selhaly. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba není zřízena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nastavení ID volajícího nesmíte měnit."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Datové připojení bylo přepnuto na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"před <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"před <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"před <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"před <xliff:g id="COUNT">%d</xliff:g> r"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> r"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"před <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"před <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"před <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"před <xliff:g id="COUNT">%d</xliff:g> r"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{před # minutou}few{před # minutami}many{před # minuty}other{před # minutami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{před # hodinou}few{před # hodinami}many{před # hodiny}other{před # hodinami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Před # dnem}few{před # dny}many{před # dne}other{před # dny}}"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e77acd09c8fe..5403cce30944 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Standarder for opkalds-id til begrænset. Næste opkald: Ikke begrænset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Begrænset"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Ikke begrænset"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af APK-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af ELF-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af APK- og ELF-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjenesten provisioneres ikke."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke ændre indstillingen for opkalds-id\'et."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Der blev skiftet til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-data"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min. siden"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> t. siden"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d. siden"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> år siden"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> t."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g> t."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. siden"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> t. siden"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d. siden"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> år siden"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{For # minut siden}one{For # minut siden}other{For # minutter siden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}one{# time siden}other{# timer siden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{1 dag siden}one{1 dag siden}other{# dag siden}}"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac0a73bcaa69..ca542ba8722b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Anrufer-ID ist standardmäßig beschränkt. Nächster Anruf: Nicht beschränkt"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Beschränkt"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Diese App ist nicht mit 16 KB kompatibel. Die APK-Abgleichsprüfung ist fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Diese App ist nicht mit 16 KB kompatibel. Die ELF-Abgleichsprüfung ist fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Diese App ist nicht mit 16 KB kompatibel. APK- und ELF-Abgleichsprüfungen sind fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dienst nicht eingerichtet."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobile Daten wurden auf <xliff:g id="CARRIERDISPLAY">%s</xliff:g> umgestellt"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g> Std."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g> T"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> J"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"vor <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"vor <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"vor <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"vor <xliff:g id="COUNT">%d</xliff:g> J."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> Min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> Std."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> J."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g> J."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"vor <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"vor <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"vor <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"vor <xliff:g id="COUNT">%d</xliff:g> J."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Vor # Minute}other{Vor # Minuten}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Vor # Stunde}other{Vor # Stunden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Vor # Tag}other{Vor # Tagen}}"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e0c2678289dc..1aae63feb546 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"περιορισμένη\". Επόμενη κλήση: Μη περιορισμένη"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"μη περιορισμένη\". Επόμενη κλήση: Περιορισμένη."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"μη περιορισμένη\". Επόμενη κλήση: Μη περιορισμένη"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Ο έλεγχος ευθυγράμμισης APK απέτυχε. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Ο έλεγχος ευθυγράμμισης ELF απέτυχε. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Οι έλεγχοι ευθυγράμμισης APK και ELF απέτυχαν. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Η υπηρεσία δεν προβλέπεται."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Έγινε εναλλαγή των δεδομένων σε <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"σε <xliff:g id="COUNT">%d</xliff:g>ώ."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"σε <xliff:g id="COUNT">%d</xliff:g>η."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"σε <xliff:g id="COUNT">%d</xliff:g>έτ."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> λ. πριν"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ω. πριν"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> η. πριν"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> χρ. πριν"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> λ."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ω."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> η."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> χρ."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"σε <xliff:g id="COUNT">%d</xliff:g> λ."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"σε <xliff:g id="COUNT">%d</xliff:g> ω."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"σε <xliff:g id="COUNT">%d</xliff:g> η."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"σε <xliff:g id="COUNT">%d</xliff:g> χρ."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> λ. πριν"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ω. πριν"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> η. πριν"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> χρ. πριν"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# λεπτό πριν}other{Πριν από # λεπτά}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Πριν από # ώρα}other{Πριν από # ώρες}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Πριν από # ημέρα}other{Πριν από # ημέρες}}"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index d17058a0de67..3c5e02ad6322 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3224c4785f4f..a2128088e715 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g>y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed4e5966a400..cf02080120e5 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index b17c07b460e2..30b60117cd19 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index e9f795ea25f0..867e8c52a4cf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"El Identificador de llamadas está predeterminado en restringido. Llamada siguiente: no restringido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"El identificador de llamadas está predeterminado en no restringido. Llamada siguiente: restringida"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El Identificador de llamadas está predeterminado en no restringido. Llamada siguiente: no restringido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación del APK. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación de ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta app no es compatible con 16 KB. No se pudieron verificar las alineaciones de APK y ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servicio no suministrado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes cambiar la configuración del identificador de llamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Se cambiaron los datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> años"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min atrás"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h atrás"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d atrás"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> años atrás"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> años"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> año"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min atrás"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h atrás"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d atrás"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> años atrás"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # de horas}other{Hace # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # de días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b7893df81fa1..1f10c5d0fb88 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"La identificación del emisor presenta el valor predeterminado de restringido. Siguiente llamada: No restringido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"La la identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: Restringido"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"La identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: No restringido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta aplicación no es compatible con el modo de 16 KB. No se ha podido comprobar la alineación del APK. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta aplicación no es compatible con el modo de 16 KB. No se ha podido comprobar la alineación de ELF. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta aplicación no es compatible con el modo de 16 KB. No se han podido comprobar las alineaciones de APK y ELF. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"El servicio no se suministra."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes modificar la identificación de emisor."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Datos cambiados a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"hace <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"hace <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"hace <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"hace <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"hace <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"hace <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"hace <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"hace <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 090d0cce6afb..58f5f20fe35f 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Helistaja ID vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Vaikimisi pole helistaja ID piiratud. Järgmine kõne: piiratud"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Helistaja ID pole vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"See rakendus ei ole suurusega 16 kB ühilduv. APK joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"See rakendus ei ole suurusega 16 kB ühilduv. ELF-i joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"See rakendus ei ole suurusega 16 kB ühilduv. APK ja ELF-i joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Teenus pole ette valmistatud."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Helistaja ID seadet ei saa muuta."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiilne andmeside lülitati operaatorile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h pärast"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> p pärast"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> a pärast"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min tagasi"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h tagasi"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> p tagasi"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> a tagasi"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> p"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min pärast"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h pärast"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> p pärast"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> a pärast"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min tagasi"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h tagasi"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> p tagasi"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> a tagasi"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minut tagasi}other{# minutit tagasi}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tund tagasi}other{# tundi tagasi}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päev tagasi}other{# päeva tagasi}}"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d27a582224a0..ab3b66b666d7 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Deitzailearen identitatea adierazteko zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenik gabe."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenekin."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APKren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu ELFaren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APK eta ELF arteko lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> operadorearen datu-konexiora aldatu zara"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h barru"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> eg. barru"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ur. barru"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Duela <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Duela <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"Duela <xliff:g id="COUNT">%d</xliff:g> egun"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"Duela <xliff:g id="COUNT">%d</xliff:g> urte"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> egun"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> urte"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min barru"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h barru"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> egun barru"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> urte barru"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Duela <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"Duela <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"Duela <xliff:g id="COUNT">%d</xliff:g> egun"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"Duela <xliff:g id="COUNT">%d</xliff:g> urte"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Duela # minutu}other{Duela # minutu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Duela # ordu}other{Duela # ordu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Duela # egun}other{Duela # egun}}"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 50c0e371aa81..954f6ba6ae1b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"پیش‌فرض شناسه تماس‌گیرنده روی محدود است. تماس بعدی: بدون محدودیت"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"پیش‌فرض شناسه تماس‌گیرنده روی غیرمحدود است. تماس بعدی: محدود"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"پیش‌فرض شناسه تماس‌گیرنده روی غیرمحدود است. تماس بعدی: بدون محدودیت"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسی تراز APK ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; مراجعه کنید"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسی تراز ELF ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; مراجعه کنید"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسی‌های تراز APK و ELF ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; مراجعه کنید"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سرویس دارای مجوز نیست."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"‏شما می‎توانید تنظیم شناسه تماس‌گیرنده را تغییر دهید."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"داده به <xliff:g id="CARRIERDISPLAY">%s</xliff:g> تغییر کرد"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"تا <xliff:g id="COUNT">%d</xliff:g> ساعت دیگر"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"تا <xliff:g id="COUNT">%d</xliff:g> روز دیگر"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"تا <xliff:g id="COUNT">%d</xliff:g> سال دیگر"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه پیش"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت پیش"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"‫<xliff:g id="COUNT">%d</xliff:g> روز پیش"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"‫<xliff:g id="COUNT">%d</xliff:g> سال پیش"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> روز"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سال"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه دیگر"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت دیگر"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"‫<xliff:g id="COUNT">%d</xliff:g> روز دیگر"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"‫<xliff:g id="COUNT">%d</xliff:g> سال دیگر"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه پیش"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت پیش"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"‫<xliff:g id="COUNT">%d</xliff:g> روز پیش"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"‫<xliff:g id="COUNT">%d</xliff:g> سال پیش"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# دقیقه قبل}one{# دقیقه قبل}other{# دقیقه قبل}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ساعت قبل}one{# ساعت قبل}other{# ساعت قبل}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# روز قبل}one{# روز قبل}other{# روز قبل}}"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7d4754126f17..96609c99cd48 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Soittajan tunnukseksi muutetaan rajoitettu. Seuraava puhelu: ei rajoitettu"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: rajoitettu"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: ei rajoitettu"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tämä sovellus ei tue 16 kt:tä. APK-sovelluksen yhteensopivuustarkistus epäonnistui. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tämä sovellus ei tue 16 kt:tä. ELF:n yhteensopivuustarkastus epäonnistui. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tämä sovellus ei tue 16 kt:tä. APK:n ja ELF:n yhteensopivuustarkastukset epäonnistuivat. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Palvelua ei tarjota."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Et voi muuttaa soittajan tunnuksen asetusta."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data vaihdettu: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h:n päästä"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> pv:n päästä"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> v:n päästä"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min sitten"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h sitten"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> pv sitten"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> v sitten"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> pv"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> v"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min päästä"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h:n päästä"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> pv:n päästä"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> v:n päästä"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min sitten"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h sitten"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> pv sitten"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> v sitten"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuutti sitten}other{# minuuttia sitten}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tunti sitten}other{# tuntia sitten}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päivä sitten}other{# päivää sitten}}"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 685d940f3509..50a769626808 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : restreint"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Cette appli n\'est pas compatible avec les pages de 16 ko. La vérification de l\'alignement de fichiers APK a échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. La vérification de l\'alignement de fichiers ELF a échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Les vérifications d\'alignement de fichiers APK et ELF ont échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Données changées à <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"il y a <xliff:g id="COUNT">%d</xliff:g> ans"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ans"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dans <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"dans <xliff:g id="COUNT">%d</xliff:g> ans"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"il y a <xliff:g id="COUNT">%d</xliff:g> ans"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e576e9287a1f..a72444d2bbd7 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : restreint"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'APK. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec des vérifications de l\'alignement de l\'APK et de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Données transférées vers <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> an"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"il y a <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dans <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"dans <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"il y a <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f2354f50a09e..27454b9319ec 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O valor predeterminado do identificador de chamada é non restrinxido. Próxima chamada: restrinxido"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros APK. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros ELF. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros APK e ELF. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizo non ofrecido."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Non podes cambiar a configuración do identificador de chamada."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Cambiouse a conexión de datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"hai <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"hai <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"hai <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"hai <xliff:g id="COUNT">%d</xliff:g> a."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"hai <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"hai <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"hai <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"hai <xliff:g id="COUNT">%d</xliff:g> a."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hai # minuto}other{Hai # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hai # hora}other{Hai # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hai # día}other{Hai # días}}"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6291688d133b..4e4c9ffe1235 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"કૉલર ID પ્રતિબંધિત પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"આ ઍપ 16 KB સુસંગત નથી. APK સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; જુઓ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"આ ઍપ 16 KB સુસંગત નથી. ELF સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; જુઓ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"આ ઍપ 16 KB સુસંગત નથી. APK અને ELF સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; જુઓ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"સેવાની જોગવાઈ કરી નથી."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"તમે કૉલર ID સેટિંગ બદલી શકતાં નથી."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ડેટા <xliff:g id="CARRIERDISPLAY">%s</xliff:g> પર સ્વિચ કર્યો"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> કલાકમાં"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> દિવસમાં"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> વર્ષમાં"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ પહેલાં"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> કલાક પહેલાં"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> દિવસ પહેલાં"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ પહેલાં"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> કલાક"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> દિવસ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> મિનિટમાં"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> કલાકમાં"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> દિવસમાં"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> વર્ષમાં"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ પહેલાં"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> કલાક પહેલાં"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> દિવસ પહેલાં"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ પહેલાં"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# મિનિટ પહેલાં}one{# મિનિટ પહેલાં}other{# મિનિટ પહેલાં}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# કલાક પહેલાં}one{# કલાક પહેલાં}other{# કલાક પહેલાં}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# દિવસ પહેલાં}one{# દિવસ પહેલાં}other{# દિવસ પહેલાં}}"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 75c8bd616bf1..624d7a507c05 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित है. अगली कॉल: सीमित नहीं"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित नहीं"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. ईएलएफ़ अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK और ईएलएफ़ के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवा प्रावधान की हुई नहीं है."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"आप कॉलर आईडी सेटिंग नहीं बदल सकते."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"डेटा को <xliff:g id="CARRIERDISPLAY">%s</xliff:g> पर स्विच किया गया"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घंटे में"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिन में"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> साल में"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> मिनट पहले"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> घंटे पहले"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> दिन पहले"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> साल पहले"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> मिनट"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> घंटे"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> दिन"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> साल"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> मिनट में"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> घंटे में"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> दिन में"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> साल में"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> मिनट पहले"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> घंटे पहले"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> दिन पहले"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> साल पहले"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनट पहले}one{# मिनट पहले}other{# मिनट पहले}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घंटा पहले}one{# घंटा पहले}other{# घंटे पहले}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिन पहले}one{# दिन पहले}other{# दिन पहले}}"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0516c4634ed2..3429c140e70a 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zadana postavka ID-a pozivatelja ima ograničenje. Sljedeći poziv: Nije ograničen"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Ograničen"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Nije ograničen"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja APK-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja ELF-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjere poravnanja APK-a i ELF-a nisu uspjele. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije rezervirana."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavku ID-a pozivatelja."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"prije <xliff:g id="COUNT">%d</xliff:g> dana"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"prije <xliff:g id="COUNT">%d</xliff:g> dana"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e980141bcd43..2d46a3cb2929 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"A hívóazonosító alapértelmezett értéke korlátozott. Következő hívás: nem korlátozott"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: korlátozott"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: nem korlátozott"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az APK-igazítási ellenőrzés sikertelen volt. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az ELF-igazítási ellenőrzés sikertelen. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az APK- és ELF-igazítási ellenőrzések sikertelenek voltak. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"A szolgáltatás nincs biztosítva."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nem tudja módosítani a hívó fél azonosítója beállítást."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Adatforgalom átváltva a következőre: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ó múlva"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> n múlva"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> é múlva"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> perce"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> órája"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> napja"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> éve"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> perc"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> óra"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> n"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> év"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> perc múlva"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ó múlva"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> n múlva"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> év múlva"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> perce"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> órája"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> napja"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> éve"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# perce}other{# perce}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# órája}other{# órája}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# napja}other{# napja}}"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index f734a5e2bc67..82d141f26af9 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Զանգողի ID-ն լռելյայն սահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` Սահմանափակված"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ ELF-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի և ELF-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ծառայությունը չի տրամադրվում:"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Դուք չեք կարող փոխել զանգողի ID-ի կարգավորումները:"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Օգտագործվում է <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-ի բջջային ինտերնետը"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ժամից"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> օրից"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> տարուց"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ր առաջ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ժ առաջ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> օր առաջ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> տ առաջ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> րոպե"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ժ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> օր"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> տ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> րոպեից"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ժամից"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> օրից"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> տարուց"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ր առաջ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ժ առաջ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> օր առաջ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> տ առաջ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# րոպե առաջ}one{# րոպե առաջ}other{# րոպե առաջ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ժամ առաջ}one{# ժամ առաջ}other{# ժամ առաջ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# օր առաջ}one{# օր առաջ}other{# օր առաջ}}"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 33126621a96d..63bc9cf223af 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID penelepon diatur default ke \"dibatasi\". Panggilan selanjutnya: Tidak dibatasi."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Dibatasi"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Tidak dibatasi"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan APK gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan ELF gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan APK dan ELF gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Layanan tidak diperlengkapi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak dapat mengubah setelan ID penelepon."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mengalihkan data seluler ke <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dalam <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dalam <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dalam <xliff:g id="COUNT">%d</xliff:g> t"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> menit lalu"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> jam lalu"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> hr lalu"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> tahun lalu"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> mnt"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> jam"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> hari"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> tahun"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dalam <xliff:g id="COUNT">%d</xliff:g> menit"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"dalam <xliff:g id="COUNT">%d</xliff:g> jam"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dalam <xliff:g id="COUNT">%d</xliff:g> hari"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"dalam <xliff:g id="COUNT">%d</xliff:g> tahun"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> menit lalu"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> jam lalu"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> hr lalu"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> tahun lalu"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# menit lalu}other{# menit lalu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# jam lalu}other{# jam lalu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# hari lalu}other{# hari lalu}}"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index cbf9798cbbd5..1b4309908629 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"eftir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"eftir <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"eftir <xliff:g id="COUNT">%d</xliff:g> ár"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"fyrir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"fyrir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"fyrir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"fyrir <xliff:g id="COUNT">%d</xliff:g> á."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> mín."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> klst."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ár"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"eftir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"eftir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"eftir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"eftir <xliff:g id="COUNT">%d</xliff:g> ár"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"fyrir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"fyrir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"fyrir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"fyrir <xliff:g id="COUNT">%d</xliff:g> árum"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fyrir # mínútu}one{Fyrir # mínútu}other{Fyrir # mínútum}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fyrir # klukkustund}one{Fyrir # klukkustund}other{Fyrir # klukkustundum}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fyrir # degi}one{Fyrir # degi}other{Fyrir # dögum}}"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 747212abac5e..9fd7913c934c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1159,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"tra <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min fa"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h fa"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> g fa"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> anni fa"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> g"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> anno"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"tra <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"tra <xliff:g id="COUNT">%d</xliff:g> anno"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min fa"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h fa"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> g fa"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> anni fa"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# di minuti fa}other{# minuti fa}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# di ore fa}other{# ore fa}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# di giorni fa}other{# giorni fa}}"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index afdd1578454b..3a5c63c8b7e7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"שירות השיחה המזוהה עובר כברירת מחדל למצב מוגבל. השיחה הבאה: לא מוגבלת"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"שירות \'שיחה מזוהה\' עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: מוגבלת"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"זיהוי מתקשר עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: לא מוגבלת"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-ELF נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK ול-ELF נכשלו. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"השירות לא הוקצה."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"אינך יכול לשנות את הגדרת זיהוי המתקשר."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"הנתונים עברו אל <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"בעוד <xliff:g id="COUNT">%d</xliff:g> שע‘"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"בעוד <xliff:g id="COUNT">%d</xliff:g> י‘"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"בעוד <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"לפני <xliff:g id="COUNT">%d</xliff:g>דק\'"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"לפני <xliff:g id="COUNT">%d</xliff:g>שע\'"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"לפני <xliff:g id="COUNT">%d</xliff:g>י\'"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"לפני <xliff:g id="COUNT">%d</xliff:g>שנ\'"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"בעוד <xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"בעוד <xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"בעוד <xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"בעוד <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"לפני <xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"לפני <xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"לפני <xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"לפני <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{לפני דקה}one{לפני # דקות}two{לפני # דקות}other{לפני # דקות}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{לפני שעה}one{לפני # שעות}two{לפני שעתיים}other{לפני # שעות}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{לפני יום}one{לפני # ימים}two{לפני יומיים}other{לפני # ימים}}"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5139e2a1b148..3d7c91cdbcf2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 時間後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 日後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 時間前"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 日前"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 時間"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 日"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"あと <xliff:g id="COUNT">%d</xliff:g> 分"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"あと <xliff:g id="COUNT">%d</xliff:g> 時間"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"あと <xliff:g id="COUNT">%d</xliff:g> 日"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"あと <xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 時間前"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 日前"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分前}other{# 分前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 時間前}other{# 時間前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 日前}other{# 日前}}"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 1a0ba5a2fd09..10cb0c3b87eb 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> საათში"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> დღეში"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> წელში"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ის წინ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ის წინ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> დღის წინ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> წლის წინ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> წთ."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> სთ."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> დღე"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> წელი"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ში"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ში"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> დღეში"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> წელში"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ის წინ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ის წინ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> დღის წინ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> წლის წინ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# წუთის წინ}other{# წუთის წინ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# საათის წინ}other{# საათის წინ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# დღის წინ}other{# დღის წინ}}"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index fd235c2e4d94..49b4bfc77b42 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелген. Келесі қоңырау: Шектелмеген"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелген"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. APK туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; сілтемесін қарап шығыңыз."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. ELF туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; сілтемесін қарап шығыңыз."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. APK және ELF файлдарының туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; сілтемесін қарап шығыңыз."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Деректер <xliff:g id="CARRIERDISPLAY">%s</xliff:g> операторына ауыстырылды"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> сағ кейін"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> күннен кейін"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> жылдан кейін"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мин бұрын"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> сағ бұрын"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> күн бұрын"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> жыл бұрын"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> сағ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> күн"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> жыл"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> минуттан кейін"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> сағаттан кейін"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> күннен кейін"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> жылдан кейін"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мин бұрын"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> сағ бұрын"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> күн бұрын"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> жыл бұрын"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минут бұрын}other{# минут бұрын}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# сағат бұрын}other{# сағат бұрын}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# күн бұрын}other{# күн бұрын}}"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2fddb5f21dbb..2db0fcd0999b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"មិន​បាន​ដាក់កម្រិត​លំនាំដើម​លេខ​សម្គាល់​អ្នក​ហៅ។ ការ​ហៅ​បន្ទាប់៖ មិន​បាន​ដាក់​កម្រិត។"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"មិន​បាន​ដាក់​កម្រិត​លេខ​សម្គាល់​អ្នក​ហៅ​លំនាំ​ដើម។ ការ​ហៅ​បន្ទាប់៖​ បាន​ដាក់កម្រិត"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"មិន​បាន​ដាក់កម្រិត​លំនាំដើម​លេខ​សម្គាល់​អ្នក​ហៅ។ ការ​ហៅ​បន្ទាប់៖ មិន​បាន​ដាក់​កម្រិត។"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម APK មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម ELF មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម APK និង ELF មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"មិន​បាន​ផ្ដល់​សេវាកម្ម។"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"អ្នក​មិន​អាច​ប្ដូរ​ការ​កំណត់​លេខ​សម្គាល់​អ្នក​ហៅ​បានទេ។"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"បានប្ដូរទិន្នន័យទៅ <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ម៉"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ថ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ឆ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> នាទីមុន"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ម៉ោងមុន"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃមុន"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំមុន"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ន"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ម៉"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> នាទី"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ម៉ោង"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ថ្ងៃ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ឆ្នាំ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> នាទីមុន"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ម៉ោងមុន"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃមុន"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំមុន"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# នាទី​មុន}other{# នាទីមុន}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ម៉ោងមុន}other{# ម៉ោងមុន}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ថ្ងៃមុន}other{# ថ្ងៃមុន}}"</string>
@@ -1224,7 +1237,7 @@
<string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ផ្កាយមួយ​ក្នុងចំណោមផ្កាយ {max}}other{ផ្កាយ # ក្នុងចំណោមផ្កាយ {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %%1$s"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"បើក​ជា​មួយ"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"បើក​ជាមួយ %1$s"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 4264717d1bf9..ad62f42ef8dc 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -561,13 +561,13 @@
<string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"ಟ್ಯಾಬ್ಲೆಟ್‌ನ ಇನ್‌ಫ್ರಾರೆಡ್ ಸಂವಾಹಕವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"ನಿಮ್ಮ Android TV ಸಾಧನದ ಇನ್‌ಫ್ರಾರೆಡ್ ಟ್ರಾನ್ಸ್‌ಮೀಟರ್ ಅನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"ಫೋನ್‌ನ ಇನ್‌ಫ್ರಾರೆಡ್ ಸಂವಾಹಕವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_setWallpaper" msgid="6959514622698794511">"ವಾಲ್‌ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setWallpaper" msgid="6959514622698794511">"ವಾಲ್‌ಪೇಪರ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"ಸಿಸ್ಟಂ ವಾಲ್‌ಪೇಪರ್‌ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"ಮರೆಮಾಡಲಾದ ಪ್ರೊಫೈಲ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
<string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"ಮರೆಮಾಡಲಾದ ಪ್ರೊಫೈಲ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"ನಿಮ್ಮ ವಾಲ್‍ಪೇಪರ್ ಗಾತ್ರವನ್ನು ಸರಿಹೊಂದಿಸಿ"</string>
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"ಸಿಸ್ಟಂ ವಾಲ್‌ಪೇಪರ್‌‌ ಗಾತ್ರದ ಸುಳಿವುಗಳನ್ನು ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_setTimeZone" msgid="7922618798611542432">"ಸಮಯದ ವಲಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setTimeZone" msgid="7922618798611542432">"ಸಮಯದ ವಲಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"ಟ್ಯಾಬ್ಲೆಟ್‌‌ನ ಸಮಯ ವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"ನಿಮ್ಮ Android TV ಸಾಧನದ ಸಮಯವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"ಫೋನ್‌ನ ಸಮಯ ವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -837,7 +837,7 @@
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"ಬಳಕೆದಾರರ ಕ್ರಿಯೆಯಿಲ್ಲದೆ ಈ ಹಿಂದೆ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಲು ಹೋಲ್ಡರ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ"</string>
<string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"ಇತರ ಆ್ಯಪ್‌ಗಳ ಮಾಲೀಕತ್ವದ E2EE ಸಂಪರ್ಕ ಕೀಗಳ ಪರಿಶೀಲನೆಯ ಸ್ಥಿತಿಗಳನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಿ"</string>
<string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"ಇತರ ಆ್ಯಪ್‌ಗಳ ಮಾಲೀಕತ್ವದ E2EE ಸಂಪರ್ಕ ಕೀಗಳ ಪರಿಶೀಲನೆಯ ಸ್ಥಿತಿಗಳನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
- <string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್‌ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್‌ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ಸ್ಕ್ರೀನ್ ಲಾಕ್‌ನಲ್ಲಿನ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಪಿನ್‌ಗಳ ಅನುಮತಿಸಲಾದ ಅಕ್ಷರಗಳ ಪ್ರಮಾಣವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್‌ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ಸ್ಕ್ರೀನ್ ಅನ್‌ಲಾಕ್‌ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್‌ ಮಾಡಿದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ, ಮತ್ತು ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ಟೈಪ್‌ ಮಾಡಿದ್ದರೆ ಟ್ಯಾಬ್ಲೆಟ್‌ ಅನ್ನು ಲಾಕ್‌ ಮಾಡಿ ಅಥವಾ ಟ್ಯಾಬ್ಲೆಟ್‌ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
@@ -863,11 +863,11 @@
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಈ Android TV ಸಾಧನದಲ್ಲಿನ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
<string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ಎಚ್ಚರಿಕೆಯಿಲ್ಲದೆ ಈ ಇನ್ಫೋಟೈನ್‌ಮೆಂಟ್ ಸಿಸ್ಟಂನಲ್ಲಿ ಈ ಪ್ರೊಫೈಲ್‌ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಫೋನ್‌ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
- <string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಹೊಂದಿಸಿ"</string>
- <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
- <string name="policylab_expirePassword" msgid="6015404400532459169">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್‌ವರ್ಡ್ ಮುಕ್ತಾಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಸೆಟ್ ಮಾಡಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
+ <string name="policylab_expirePassword" msgid="6015404400532459169">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್‌ವರ್ಡ್ ಮುಕ್ತಾಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="policydesc_expirePassword" msgid="9136524319325960675">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್‌ವರ್ಡ್, ಪಿನ್, ಅಥವಾ ನಮೂನೆಯನ್ನು ಹೆಚ್ಚು ಪದೆ ಪದೇ ಬದಲಾಯಿಸಬೇಕಾಗಿರುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಿ."</string>
- <string name="policylab_encryptedStorage" msgid="9012936958126670110">"ಸಂಗ್ರಹಣೆ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಹೊಂದಿಸಿ"</string>
+ <string name="policylab_encryptedStorage" msgid="9012936958126670110">"ಸಂಗ್ರಹಣೆ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="policydesc_encryptedStorage" msgid="1102516950740375617">"ಸಂಗ್ರಹಿಸಿರುವ ಆ್ಯಪ್ ಡೇಟಾವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಬೇಕಾದ ಅಗತ್ಯವಿದೆ."</string>
<string name="policylab_disableCamera" msgid="5749486347810162018">"ಕ್ಯಾಮರಾಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"ಎಲ್ಲಾ ಸಾಧನ ಕ್ಯಾಮರಾಗಳ ಬಳಕೆಯನ್ನು ತಡೆಯಿರಿ."</string>
@@ -1103,7 +1103,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"ಈ ಪುಟದಲ್ಲಿಯೇ ಇರಿ"</string>
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nನೀವು ಈ ಪುಟದಿಂದಾಚೆಗೆ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಲು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?"</string>
<string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> ಸಹಾಯದಿಂದ ಸ್ವಯಂ-ಭರ್ತಿ"</string>
- <string name="permlab_setAlarm" msgid="1158001610254173567">"ಅಲಾರಮ್ ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setAlarm" msgid="1158001610254173567">"ಅಲಾರಮ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"ಸ್ಥಾಪಿಸಲಾದ ಅಲಾರಮ್ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್‌ನಲ್ಲಿ ಅಲಾರಮ್ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಕೆಲವು ಅಲಾರಮ್ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸದಿರಬಹುದು."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"ಧ್ವನಿಮೇಲ್ ಸೇರಿಸಿ"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"ನಿಮ್ಮ ದ್ವನಿಮೇಲ್‌ ಇನ್‌‌ಬಾಕ್ಸ್‌‌ಗೆ ಸಂದೇಶಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>ಗಂ ಯಲ್ಲಿ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>ದಿ ದಲ್ಲಿ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>ವ ದಲ್ಲಿ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದ ಹಿಂದೆ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ ಹಿಂದೆ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>ದಿನಗಳ ಹಿಂದೆ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದ ಹಿಂದೆ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ದಿನ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದಲ್ಲಿ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆಯಲ್ಲಿ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>ದಿನದಲ್ಲಿ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದಲ್ಲಿ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದ ಹಿಂದೆ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ ಹಿಂದೆ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>ದಿನಗಳ ಹಿಂದೆ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದ ಹಿಂದೆ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ನಿಮಿಷದ ಹಿಂದೆ}one{# ನಿಮಿಷಗಳ ಹಿಂದೆ}other{# ನಿಮಿಷಗಳ ಹಿಂದೆ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ಗಂಟೆಯ ಹಿಂದೆ}one{# ಗಂಟೆಗಳ ಹಿಂದೆ}other{# ಗಂಟೆಗಳ ಹಿಂದೆ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ದಿನದ ಹಿಂದೆ}one{# ದಿನಗಳ ಹಿಂದೆ}other{# ದಿನಗಳ ಹಿಂದೆ}}"</string>
@@ -1389,7 +1405,7 @@
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"ಆ್ಯಪ್‌ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"ಹೊಸ ಸಿಮ್ ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"ಇದನ್ನು ಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_picker_dialog_title" msgid="5030520449243071926">"ದಿನಾಂಕವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_time_set" msgid="4603445265164486816">"ಹೊಂದಿಸು"</string>
<string name="date_time_done" msgid="8363155889402873463">"ಆಯಿತು"</string>
@@ -1590,7 +1606,7 @@
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"ಗಂಟೆಯನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
<string name="time_picker_decrement_hour_button" msgid="584101766855054412">"ಗಂಟೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"PM ಹೊಂದಿಸು"</string>
- <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"AM ಹೊಂದಿಸಿ"</string>
+ <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"AM ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_picker_increment_month_button" msgid="3447263316096060309">"ತಿಂಗಳನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
<string name="date_picker_decrement_month_button" msgid="6531888937036883014">"ತಿಂಗಳು ಕಡಿಮೆಮಾಡಿ"</string>
<string name="date_picker_increment_day_button" msgid="4349336637188534259">"ದಿನವನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
@@ -2076,7 +2092,7 @@
<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>
+ <string name="time_picker_header_text" msgid="9073802285051516688">"ಸಮಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="time_picker_input_error" msgid="8386271930742451034">"ಮಾನ್ಯವಾದ ಸಮಯವನ್ನು ನಮೂದಿಸಿ"</string>
<string name="time_picker_prompt_label" msgid="303588544656363889">"ಸಮಯ ಟೈಪ್ ಮಾಡಿ"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"ಸಮಯವನ್ನು ನಮೂದಿಸಲು ಪಠ್ಯದ ನಮೂನೆಗೆ ಬದಲಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b8bbacba554e..38e5bb573222 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한됨"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"이 앱은 16KB와 호환되지 않습니다. APK 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;를 참고하세요."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"이 앱은 16KB와 호환되지 않습니다. ELF 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;를 참고하세요."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"이 앱은 16KB와 호환되지 않습니다. APK 및 ELF 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;를 참고하세요."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"서비스가 준비되지 않았습니다."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"발신자 번호 설정을 변경할 수 없습니다."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> 이동통신사로 데이터가 변경됨"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>일 후"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>년 후"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>분 전"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>일 전"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>년 전"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>분"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>시간"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>일"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>년"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>분 후"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>일 후"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>년 후"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>분 전"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>일 전"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>년 전"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{#분 전}other{#분 전}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{#시간 전}other{#시간 전}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{#일 전}other{#일 전}}"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 70c4d8eefd02..60e2749d9f45 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> с. кийин"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> к. кийин"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ж. кийин"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мүн. мурун"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> с. мурун"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> к. мурун"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ж. мурун"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мүн."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> с."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> к."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ж."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> мүн. кийин"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> с. кийин"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> к. кийин"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ж. кийин"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мүн. мурун"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> с. мурун"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> к. мурун"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ж. мурун"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# мүнөт мурун}other{# мүнөт мурун}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# саат мурун}other{# саат мурун}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# күн мурун}other{# күн мурун}}"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 837cfc3d2cac..da2d1ad8ac86 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ຊມ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ມ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ປ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ນາທີກ່ອນ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງກ່ອນ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ມື້ກ່ອນ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ປີກ່ອນ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ນາທີ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ມື້"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ປີ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ນາທີ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ມື້"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ປີ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ນາທີກ່ອນ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງກ່ອນ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ມື້ກ່ອນ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ປີກ່ອນ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ນາທີກ່ອນ}other{# ນາທີກ່ອນ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ຊົ່ວໂມງກ່ອນ}other{# ຊົ່ວໂມງກ່ອນ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ມື້ກ່ອນ}other{# ມື້ກ່ອນ}}"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a2813558aa3c..5de98814032f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1160,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"po <xliff:g id="COUNT">%d</xliff:g> val."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"po <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"po <xliff:g id="COUNT">%d</xliff:g> m."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prieš <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prieš <xliff:g id="COUNT">%d</xliff:g> val."</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"prieš <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"prieš <xliff:g id="COUNT">%d</xliff:g> m."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> val."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> m."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"po <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"po <xliff:g id="COUNT">%d</xliff:g> val."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"po <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"po <xliff:g id="COUNT">%d</xliff:g> m."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prieš <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"prieš <xliff:g id="COUNT">%d</xliff:g> val."</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"prieš <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"prieš <xliff:g id="COUNT">%d</xliff:g> m."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prieš # minutę}one{Prieš # minutę}few{Prieš # minutes}many{Prieš # minutės}other{Prieš # minučių}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prieš # valandą}one{Prieš # valandą}few{Prieš # valandas}many{Prieš # valandos}other{Prieš # valandų}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prieš # dieną}one{Prieš # dieną}few{Prieš # dienas}many{Prieš # dienos}other{Prieš # dienų}}"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a1c134b0dba6..ac109a87e88d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zvanītāja ID noklusējumi ir iestatīti uz Ierobežots. Nākamais zvans: nav ierobežots"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: ierobežots"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: nav ierobežots"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Šī lietotne nav saderīga ar 16 KB lapām. APK saskaņošanas pārbaude neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Šī lietotne nav saderīga ar 16 KB lapām. ELF saskaņošanas pārbaude neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Šī lietotne nav saderīga ar 16 KB lapām. APK un ELF saskaņošanas pārbaudes neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Pakalpojums netiek nodrošināts."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Zvanītāja ID iestatījumu nevar mainīt."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Tiek izmantots operatora <xliff:g id="CARRIERDISPLAY">%s</xliff:g> datu savienojums"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"pēc <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"pēc <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"pēc <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pirms <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pirms <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"pirms <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"pirms <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"pēc <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"pēc <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"pēc <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"pēc <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pirms <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"pirms <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"pirms <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"pirms <xliff:g id="COUNT">%d</xliff:g> g."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pirms vienas minūtes}zero{Pirms # minūtēm}one{Pirms vairākām minūtēm, minūšu skaits: #}other{Pirms vairākām minūtēm, minūšu skaits: #}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pirms vienas stundas}zero{Pirms # stundām}one{Pirms vairākām stundām, stundu skaits: #}other{Pirms vairākām stundām, stundu skaits: #}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pirms vienas dienas}zero{Pirms # dienām}one{Pirms vairākām dienām, dienu skaits: #}other{Pirms vairākām dienām, dienu skaits: #}}"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 74e5bf00e704..1f30da3e7305 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Стандардно, ID на повикувач е скриен. Следен повик: не е скриен"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Стандардно, ID на повикувач не е скриен. Следен повик: скриен"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандардно, ID на повикувач не е скриен. Следен повик: не е скриен"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на АПК не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на ELF не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Апликацијава не е компатибилна со 16 KB. Проверките за усогласување на АПК и ELF се неуспешни. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е предвидена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не може да го промените поставувањето за ID на повикувач."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилниот интернет се префрли на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"по <xliff:g id="COUNT">%d</xliff:g> ч."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"по <xliff:g id="COUNT">%d</xliff:g> д."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"по <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"пред <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"пред <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"пред <xliff:g id="COUNT">%d</xliff:g> д."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"пред <xliff:g id="COUNT">%d</xliff:g> год."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> год."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"по <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"по <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"по <xliff:g id="COUNT">%d</xliff:g> д."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"по <xliff:g id="COUNT">%d</xliff:g> год."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"пред <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"пред <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"пред <xliff:g id="COUNT">%d</xliff:g> д."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"пред <xliff:g id="COUNT">%d</xliff:g> год."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пред # минута}one{Пред # минута}other{Пред # минути}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пред # час}one{Пред # час}other{Пред # часа}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пред # ден}one{Пред # ден}other{Пред # дена}}"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 37f51c5f80f1..7f8027dff916 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"നിയന്ത്രിക്കേണ്ട സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടില്ല"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"നിയന്ത്രിക്കേണ്ടതല്ലാത്ത സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടുണ്ട്"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"നിയന്ത്രിക്കേണ്ടതല്ലാത്ത സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടില്ല"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. APK അലൈൻമെന്റ് പരിശോധന നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; കാണുക"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. ELF അലൈൻമെന്റ് പരിശോധന നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; കാണുക"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. APK, ELF അലൈൻമെന്റ് പരിശോധനകൾ നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; കാണുക"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"സേവനം വ്യവസ്ഥ ചെയ്‌തിട്ടില്ല."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"വിളിച്ച നമ്പർ ക്രമീകരണം നിങ്ങൾക്ക് മാറ്റാനാവില്ല."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> എന്നതിലേക്ക് ഡാറ്റ മാറ്റി"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂറിൽ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>ദിവസത്തിൽ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>വർഷത്തിനുള്ളിൽ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m മുമ്പ്"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h മുമ്പ്"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം മുമ്പ്"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>വർഷം മുമ്പ്"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റ്"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂർ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>വർഷം"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റിൽ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂറിൽ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>ദിവസത്തിനുള്ളിൽ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>വർഷത്തിൽ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റ് മുമ്പ്"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂർ മുമ്പ്"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം മുമ്പ്"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>വർഷം മുമ്പ്"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# മിനിറ്റ് മുമ്പ്}other{# മിനിറ്റ് മുമ്പ്}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# മണിക്കൂർ മുമ്പ്}other{# മണിക്കൂർ മുമ്പ്}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ദിവസം മുമ്പ്}other{# ദിവസം മുമ്പ്}}"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index fce948ec645a..8043baec1b4e 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдаагүй"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Дуудлага хийгчийн ID хязгаарлагдаагүй. Дараагийн дуудлага: Хязгаарлагдсан"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдсан"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. APK эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; холбоосыг харна уу"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. ELF эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; холбоосыг харна уу"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. APK, ELF эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; холбоосыг харна уу"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Үйлчилгээ провишн хийгдээгүй ."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Та дуудлага хийгчийн ID тохиргоог солиж чадахгүй."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Өгөгдлийг <xliff:g id="CARRIERDISPLAY">%s</xliff:g> руу шилжүүлсэн"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>цагийн дараа"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>хоногийн дараа"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>жилийн дараа"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> минутын өмнө"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> цагийн өмнө"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> хоногийн өмнө"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> жилийн өмнө"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> минут"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> цаг"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> хоног"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> жил"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> минутын дараа"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> цагийн дараа"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> хоногийн дараа"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> жилийн дараа"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> минутын өмнө"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> цагийн өмнө"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> хоногийн өмнө"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> жилийн өмнө"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минутын өмнө}other{# минутын өмнө}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# цагийн өмнө}other{# цагийн өмнө}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# хоногийн өмнө}other{# хоногийн өмнө}}"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index cf763116b49a..7bdd94254966 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> तासांमध्ये"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिवसांमध्ये"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षांमध्ये"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>मिनिटापूर्वी"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>तासापूर्वी"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>दिवसापूर्वी"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>वर्षापूर्वी"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>मिनिट"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>तास"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>दिवस"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>वर्ष"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>मिनिटामध्‍ये"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>तासामध्ये"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>दिवसामध्ये"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>वर्षामध्ये"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>मिनिटापूर्वी"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>तासापूर्वी"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>दिवसापूर्वी"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>वर्षापूर्वी"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनिटापूर्वी}other{# मिनिटांपूर्वी}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# तासापूर्वी}other{# तासांपूर्वी}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिवसापूर्वी}other{# दिवसांपूर्वी}}"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 34d765486e3a..21925f8aaab1 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID pemanggil secara lalainya ditetapkan kepada terhad. Panggilan seterusnya: Tidak terhad"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID pemanggil secara lalainya ditetapkan kepada tidak terhad. Panggilan seterusnya: Terhad"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pemanggil secara lalainya ditetapkan kepada tidak dihadkan. Panggilan seterusnya: Tidak terhad"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran APK gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran ELF gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran APK dan ELF gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Perkhidmatan yang tidak diuntukkan."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak boleh mengubah tetapan ID pemanggil."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data ditukar kepada <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dalam <xliff:g id="COUNT">%d</xliff:g>j"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dalam <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dalam <xliff:g id="COUNT">%d</xliff:g>t"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m yang lalu"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>j yang lalu"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>h yang lalu"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>t yang lalu"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>jam"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>thn"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"selepas <xliff:g id="COUNT">%d</xliff:g>minit"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"selepas <xliff:g id="COUNT">%d</xliff:g>jam"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dalam <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"selepas <xliff:g id="COUNT">%d</xliff:g>thn"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>minit yang lalu"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>jam yang lalu"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>h yang lalu"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>thn yang lalu"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minit yang lalu}other{# minit yang lalu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# jam yang lalu}other{# jam yang lalu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# hari yang lalu}other{# hari yang lalu}}"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 45be6f5955c1..61af771b5818 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်ထားသည်။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်ထားသည်။"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK နှင့် ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ဝန်ဆောင်မှုအား ကန့်သတ်မထားပါ"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"သင်သည် ခေါ်ဆိုသူ ID ဆက်တင်ကို မပြောင်းလဲနိုင်ပါ။"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ဒေတာကို <xliff:g id="CARRIERDISPLAY">%s</xliff:g> သို့ ပြောင်းထားသည်"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> နာရီအတွင်း"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ရက်အတွင်း"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> နှစ်အတွင်း"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> မိနစ်အတွင်း"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> နာရီအတွင်း"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ရက်အတွင်း"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> နှစ်အတွင်း"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ပြီးခဲ့သော # မိနစ်}other{ပြီးခဲ့သော # မိနစ်}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ပြီးခဲ့သော # နာရီ}other{ပြီးခဲ့သော # နာရီ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ပြီးခဲ့သော # ရက်}other{ပြီးခဲ့သော # ရက်}}"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 910d70d5bbfb..0ddbc41a8cec 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nummervisning er begrenset som standard. Neste anrop: Ikke begrenset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Nummervisning er ikke begrenset som standard. Neste anrop: Begrenset"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummervisning er ikke begrenset som standard. Neste anrop: Ikke begrenset"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Denne appen er ikke kompatibel med 16 kB. Kontrollen av APK-justering mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Denne appen er ikke kompatibel med 16 kB. Kontrollen av ELF-justering mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Denne appen er ikke kompatibel med 16 kB. APK- og ELF-justeringskontrollene mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"SIM-kortet er ikke tilrettelagt for tjenesten."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke endre innstillingen for anrops-ID."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Byttet data til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"for <xliff:g id="COUNT">%d</xliff:g>m siden"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"for <xliff:g id="COUNT">%d</xliff:g>t siden"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"for <xliff:g id="COUNT">%d</xliff:g>d siden"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"for <xliff:g id="COUNT">%d</xliff:g> år siden"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>t"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g>t"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"for <xliff:g id="COUNT">%d</xliff:g> min siden"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"for <xliff:g id="COUNT">%d</xliff:g>t siden"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"for <xliff:g id="COUNT">%d</xliff:g>d siden"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"for <xliff:g id="COUNT">%d</xliff:g> år siden"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{for # minutt siden}other{For # minutter siden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}other{# timer siden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{For # dag siden}other{For # dager siden}}"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 5e6d8507772b..c9a922eae470 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"कलर ID पूर्वनिर्धारितको लागि रोकावट छ। अर्को कल: रोकावट छैन"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कलर ID पूर्वनिर्धारितदेखि प्रतिबन्धित छैन। अर्को कल: प्रतिबन्धित छ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। APK को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; हेर्नुहोस्"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। ELF को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; हेर्नुहोस्"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। APK र ELF को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; हेर्नुहोस्"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> को डेटा प्रयोग गर्न थालिएको छ"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घण्टाभित्र"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिनभित्र"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षभित्र"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> मिनेटअघि"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> घण्टाअघि"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> दिनअघि"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> वर्षअघि"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> मिनेट"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> घण्टा"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> दिन"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> वर्ष"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> मिनेटपछि"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> घण्टापछि"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> दिनपछि"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> वर्षपछि"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> मिनेटअघि"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> घण्टाअघि"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> दिनअघि"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> वर्षअघि"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनेटअघि}other{# मिनेटअघि}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घण्टाअघि}other{# घण्टाअघि}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिनअघि}other{# दिनअघि}}"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b45f6cd56467..e88384b48c7f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Beller-ID standaard ingesteld op \'beperkt\'. Volgend gesprek: onbeperkt."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: beperkt."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: onbeperkt."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Deze app is niet compatibel met 16 KB. APK-uitlijningscontrole mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; voor meer informatie."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Deze app is niet compatibel met 16 KB. ELF-uitlijningscontrole mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; voor meer informatie."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Deze app is niet compatibel met 16 KB. APK- en ELF-uitlijningscontroles mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; voor meer informatie."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service niet voorzien."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiele data overgeschakeld naar <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"over <xliff:g id="COUNT">%d</xliff:g> u"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"over <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"over <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min geleden"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> u geleden"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d geleden"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> j geleden"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> u"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"over <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"over <xliff:g id="COUNT">%d</xliff:g> u"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"over <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"over <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min geleden"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> u geleden"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d geleden"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> j geleden"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut geleden}other{# minuten geleden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur geleden}other{# uur geleden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag geleden}other{# dagen geleden}}"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 9e57ca32fc04..9960691d7206 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ସେବାର ସୁବିଧା ନାହିଁ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ଆପଣ କଲର୍‍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>କୁ ଡାଟା ସ୍ୱିଚ କରାଯାଇଛି"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟାରେ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ଦିନରେ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷରେ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ ପୂର୍ବେ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା ପୂର୍ବେ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ ପୂର୍ବେ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ ପୂର୍ବେ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟରେ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟାରେ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ଦିନରେ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷରେ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ ପୂର୍ବେ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା ପୂର୍ବେ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ ପୂର୍ବେ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ ପୂର୍ବେ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ମିନିଟ ପୂର୍ବେ}other{# ମିନିଟ ପୂର୍ବେ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ଘଣ୍ଟା ପୂର୍ବେ}other{# ଘଣ୍ଟା ପୂର୍ବେ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ଦିନ ପୂର୍ବେ}other{# ଦିନ ପୂର୍ବେ}}"</string>
@@ -1671,7 +1684,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ହେଡଫୋନ୍‍"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ସିଷ୍ଟମ"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"ବ୍ଲୁଟୂଥ୍‍‌ ଅଡିଓ"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"ବ୍ଲୁଟୁଥ ଅଡିଓ"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"ୱେୟାର୍‍ଲେସ୍‍ ଡିସ୍‍ପ୍ଲେ"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"କାଷ୍ଟ କରନ୍ତୁ"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"ଡିଭାଇସ୍‍ ସଂଯୋଗ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index c7b11e5c3b51..6d92e0148d78 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ਪ੍ਰਤਿਬੰਧਿਤ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। APK ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ਦੇਖੋ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। ELF ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ਦੇਖੋ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। APK ਅਤੇ ELF ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ਦੇਖੋ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ਤੁਸੀਂ ਕਾਲਰ ਆਈ.ਡੀ. ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ਡਾਟੇ ਨੂੰ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕੀਤਾ ਗਿਆ"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਵਿੱਚ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਵਿੱਚ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਵਿੱਚ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਪਹਿਲਾਂ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਪਹਿਲਾਂ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਪਹਿਲਾਂ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਪਹਿਲਾਂ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟਾ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਵਿੱਚ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਵਿੱਚ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਵਿੱਚ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਵਿੱਚ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਪਹਿਲਾਂ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟਾ ਪਹਿਲਾਂ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਪਹਿਲਾਂ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਪਹਿਲਾਂ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ਮਿੰਟ ਪਹਿਲਾਂ}one{# ਮਿੰਟ ਪਹਿਲਾਂ}other{# ਮਿੰਟ ਪਹਿਲਾਂ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ਘੰਟਾ ਪਹਿਲਾਂ}one{# ਘੰਟਾ ਪਹਿਲਾਂ}other{# ਘੰਟੇ ਪਹਿਲਾਂ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ਦਿਨ ਪਹਿਲਾਂ}one{# ਦਿਨ ਪਹਿਲਾਂ}other{# ਦਿਨ ਪਹਿਲਾਂ}}"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7a7e159b278a..ec37251a7394 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID rozmówcy ustawiony jest domyślnie na „zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: zastrzeżony"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK i ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usługa nie jest świadczona."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Przełączono mobilną transmisję danych na: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> godz."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min temu"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> godz. temu"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d temu"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> r./l. temu"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> godz."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r./l."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> godz."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> r./l."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min temu"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> godz. temu"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d temu"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> r./l. temu"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutę temu}few{# minuty temu}many{# minut temu}other{# minuty temu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# godzinę temu}few{# godziny temu}many{# godzin temu}other{# godziny temu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dzień temu}few{# dni temu}many{# dni temu}other{# dnia temu}}"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d33277783fea..5ff383df1770 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento do APK. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Este app não é compatível com 16 KB. Falha nas verificações de alinhamento do APK e do ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"em <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 8b1d408f8b50..38a071a5cbb2 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1159,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dentro de <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"dentro de <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dentro de <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"dentro de <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Há # minuto}many{Há # minutos}other{Há # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{há # hora}many{há # horas}other{há # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Há # dia}many{Há # dias}other{Há # dias}}"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d33277783fea..5ff383df1770 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento do APK. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Este app não é compatível com 16 KB. Falha nas verificações de alinhamento do APK e do ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"em <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ed974315eb56..738951eac18b 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: Restricționat."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Această aplicație nu este compatibilă cu 16 KB. Verificarea alinierii APK nu a reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Această aplicație nu este compatibilă cu 16 KB. Verificarea alinierii ELF nu a reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Această aplicație nu este compatibilă cu 16 KB. Verificările de aliniere APK și ELF nu au reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"S-a trecut la datele mobile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"în <xliff:g id="COUNT">%d</xliff:g> ore"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"în <xliff:g id="COUNT">%d</xliff:g> z"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"în <xliff:g id="COUNT">%d</xliff:g> ani"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"acum <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"acum <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"acum <xliff:g id="COUNT">%d</xliff:g> z"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"acum <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> z"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"peste <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"peste <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"peste <xliff:g id="COUNT">%d</xliff:g> z"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"peste <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"acum <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"acum <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"acum <xliff:g id="COUNT">%d</xliff:g> z"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"acum <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Acum # minut}few{Acum # minute}other{Acum # de minute}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Acum # oră}few{Acum # ore}other{Acum # de ore}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Acum # zi}few{Acum # zile}other{Acum # de zile}}"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9b3a7199b1df..285e941070ba 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Идентификация абонента по умолчанию запрещена. След. вызов: разрешена"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Идентификация абонента по умолчанию не запрещена. След. вызов: запрещена"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Идентификация абонента по умолчанию не запрещена. След. вызов: разрешена"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в APK-файле. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в ELF-файле. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в APK- и ELF-файлах. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга не предоставляется."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Невозможно изменить параметр идентификатора вызывающего абонента."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Используется мобильный интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> ч."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мин. назад"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> дн. назад"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> г./лет назад"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> дн."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г./лет"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"через <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"через <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"через <xliff:g id="COUNT">%d</xliff:g> г./лет"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мин. назад"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> дн. назад"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> г./лет назад"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минуту назад}one{# минуту назад}few{# минуты назад}many{# минут назад}other{# минуты назад}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# час назад}one{# час назад}few{# часа назад}many{# часов назад}other{# часа назад}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день назад}one{# день назад}few{# дня назад}many{# дней назад}other{# дня назад}}"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a02cf028df8a..0cb6f3096167 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී ඇත. මීළඟ ඇමතුම: සීමා කර නැත"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"මෙම යෙදුම 16 KB අනුකූල නොවේ. APK පෙළගැස්වීමේ පරීක්ෂාව අසමත් විය. මෙම යෙදුම පිටු ප්‍රමාණයට ගැළපෙන ප්‍රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; බලන්න"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"මෙම යෙදුම 16 KB අනුකූල නොවේ. ELF පෙළගැස්වීමේ පරීක්ෂාව අසමත් විය. මෙම යෙදුම පිටු ප්‍රමාණයට ගැළපෙන ප්‍රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; බලන්න"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"මෙම යෙදුම 16 KB අනුකූල නොවේ. APK සහ ELF පෙළගැස්වීමේ පරීක්ෂාවන් අසමත් විය. මෙම යෙදුම පිටු ප්‍රමාණයට ගැළපෙන ප්‍රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; බලන්න"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"සේවාවන් සපයා නැත."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"අමතන්නාගේ ID සැකසීම ඔබට වෙනස්කල නොහැක."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"දත්ත <xliff:g id="CARRIERDISPLAY">%s</xliff:g> වෙත මාරු කරන ලදි"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"පැ<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"දි<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ව<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"මිනි <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"පැය <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"දින <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"වසර <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"මිනි <xliff:g id="COUNT">%d</xliff:g>ක්"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"පැය <xliff:g id="COUNT">%d</xliff:g>ක්"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"දින <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"වසර <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"මිනි <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"පැය <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"දින <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"වසර <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"මිනි <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"පැය <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"දින <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"වසර <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{මිනිත්තු #කට පෙර}one{මිනිත්තු #කට පෙර}other{මිනිත්තු #කට පෙර}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{පැය #කට පෙර}one{පැය #කට පෙර}other{පැය #කට පෙර}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{දින #කට පෙර}one{දින #කට පෙර}other{දින #කට පෙර}}"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index fd2c7276d615..9d0efef85a72 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"V predvolenom nastavení je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Obmedzené"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie súboru APK. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie formátu ELF. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie súboru APK a formátu ELF. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba nie je poskytovaná."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nemôžete meniť nastavenie identifikácie volajúcich."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dátové pripojenie bolo prepnuté na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"o <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"o <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"o <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"Pred <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"Pred <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"O <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"O <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"O <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"O <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"Pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"Pred <xliff:g id="COUNT">%d</xliff:g> d."</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"Pred <xliff:g id="COUNT">%d</xliff:g> r."</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pred # minútou}few{Pred # minútami}many{Pred # minúty}other{Pred # minútami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pred # hodinou}few{Pred # hodinami}many{Pred # hodiny}other{Pred # hodinami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pred # dňom}few{Pred # dňami}many{Pred # dňa}other{Pred # dňami}}"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 51803b098adb..e7e96f50297a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1160,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"čez <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"čez <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"čez <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"pred <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"pred <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"čez <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"čez <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"čez <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"čez <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"pred <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"pred <xliff:g id="COUNT">%d</xliff:g> l"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pred # minuto}one{Pred # minuto}two{Pred # minutama}few{Pred # minutami}other{Pred # minutami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pred # uro}one{Pred # uro}two{Pred # urama}few{Pred # urami}other{Pred # urami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pred # dnevom}one{Pred # dnevom}two{Pred # dnevoma}few{Pred # dnevi}other{Pred # dnevi}}"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 995910a87045..744b5577904f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të kufizuar. Telefonata e radhës: e pakufizuar!"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e kufizuar!"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e pakufizuar!"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrolli i drejtvendosjes për APK dështoi. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrolli i drejtvendosjes për ELF dështoi. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrollet e drejtvendosjes për APK dhe ELF dështuan. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Shërbimi nuk është përgatitur."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nuk mund ta ndryshosh cilësimin e ID-së së telefonuesit."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Të dhënat u kaluan te <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"për <xliff:g id="COUNT">%d</xliff:g> orë"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"për <xliff:g id="COUNT">%d</xliff:g> ditë"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"për <xliff:g id="COUNT">%d</xliff:g> vit"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min. më parë"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> orë më parë"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ditë më parë"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> vite më parë"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> orë"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ditë"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> vite"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"për <xliff:g id="COUNT">%d</xliff:g> min."</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"për <xliff:g id="COUNT">%d</xliff:g> orë"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"për <xliff:g id="COUNT">%d</xliff:g> ditë"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"për <xliff:g id="COUNT">%d</xliff:g> vite"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. më parë"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> orë më parë"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ditë më parë"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> vite më parë"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutë më parë}other{# minuta më parë}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# orë më parë}other{# orë më parë}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ditë më parë}other{# ditë më parë}}"</string>
@@ -1671,7 +1684,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kufjet"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistemi"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e Bluetooth-it"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja me Bluetooth"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"Ekran wireless"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"Transmeto"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"Lidhu me pajisjen"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7ce7b7b47b76..45fb8ee60e1e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ИД позиваоца је подразумевано ограничен. Следећи позив: Није ограничен."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ИД позиваоца подразумевано није ограничен. Следећи позив: ограничен."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ИД позиваоца подразумевано није ограничен. Следећи позив: Није ограничен."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ова апликација није компатибилна са величином странице од 16 kB. Провера усклађености APK-а није успела. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ова апликација није компатибилна са величином странице од 16 kB. Провера усклађености ELF-а није успела. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ова апликација није компатибилна са величином странице од 16 kB. Провере усклађености APK-а и ELF-а нису успеле. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга није добављена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да промените подешавање ИД-а корисника."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилни подаци су пребачени на оператера <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"пре <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"пре <xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"пре <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"пре <xliff:g id="COUNT">%d</xliff:g> г"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"за <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"пре <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"пре <xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"пре <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"пре <xliff:g id="COUNT">%d</xliff:g> год"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пре # минут}one{Пре # минут}few{Пре # минута}other{Пре # минута}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пре # сат}one{Пре # сат}few{Пре # сата}other{Пре # сати}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пре # дан}one{Пре # дан}few{Пре # дана}other{Пре # дана}}"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a9a606721d12..4b22b7df0c8c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nummerpresentatörens standardinställning är blockerad. Nästa samtal: Inte blockerad"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Blockerad"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Inte blockerad"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Den här appen är inte kompatibel med 16 KB. APK-justeringskontrollen misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Den här appen är inte kompatibel med 16 KB. ELF-justeringskontrollen misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Den här appen är inte kompatibel med 16 kB. APK- och ELF-justeringskontrollerna misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjänsten är inte etablerad."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Det går inte att ändra inställningen för nummerpresentatör."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Du har bytt mobildata till <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> tim"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"för <xliff:g id="COUNT">%d</xliff:g> m sedan"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"för <xliff:g id="COUNT">%d</xliff:g> h sedan"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"för <xliff:g id="COUNT">%d</xliff:g> d sedan"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"för <xliff:g id="COUNT">%d</xliff:g> år sedan"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"för <xliff:g id="COUNT">%d</xliff:g> min sedan"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"för <xliff:g id="COUNT">%d</xliff:g> h sedan"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"för <xliff:g id="COUNT">%d</xliff:g> d sedan"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"för <xliff:g id="COUNT">%d</xliff:g> år sedan"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{För # minut sedan}other{För # minuter sedan}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{För # timme sedan}other{För # timmar sedan}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{För # dag sedan}other{För # dagar sedan}}"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 4d0d7dc7b465..d6565bfe33aa 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Chaguomsingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa APK. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; upate maelezo zaidi"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa ELF. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; upate maelezo zaidi"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa APK na ELF. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; upate maelezo zaidi"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Huduma haitathminiwi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Sasa unatumia data ya mtandao wa <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"baada ya saa <xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"baada ya siku <xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"baada ya mwaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"dak <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"saa <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"siku <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"miaka <xliff:g id="COUNT">%d</xliff:g> iliyopita"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"dak <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"saa <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"siku <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"miaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ndani ya dak <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"ndani ya saa <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"ndani ya siku <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"ndani ya miaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"dak <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"saa <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"siku <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"miaka <xliff:g id="COUNT">%d</xliff:g> iliyopita"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Dakika # iliyopita}other{Dakika # zilizopita}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Saa # iliyopita}other{Saa # zilizopita}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Siku # iliyopita}other{Siku # zilizopita}}"</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54be493..0b0a4cb5cd17 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -47,7 +47,7 @@
<bool name="config_navBarCanMove">false</bool>
<!-- Set to true to enable the user switcher on the keyguard. -->
- <bool name="config_keyguardUserSwitcher">true</bool>
+ <bool name="config_keyguardUserSwitcher">false</bool>
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 96e62f20e6e2..17119a6b974e 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்பட்டது என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்பட்டது"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. APK சீரமைவைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; எனும் பக்கத்தைப் பார்க்கவும்."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. ELF சீரமைவைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; எனும் பக்கத்தைப் பார்க்கவும்."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. APK மற்றும் ELF சீரமைவுகளைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; எனும் பக்கத்தைப் பார்க்கவும்."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"சேவை ஒதுக்கப்படவில்லை."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"அழைப்பாளர் ஐடி அமைப்பை மாற்ற முடியாது."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"டேட்டா <xliff:g id="CARRIERDISPLAY">%s</xliff:g>க்கு மாற்றப்பட்டது"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்தில்"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> நாட்களில்"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டில்"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> நி முன்பு"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ம.நே முன்பு"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> நா முன்பு"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டு முன்பு"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> நிமிடம்"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ம.நே"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> நா"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டு"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> நிமிடத்தில்"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்தில்"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> நாளில்"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டில்"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> நிமிடம் முன்பு"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ம.நே முன்பு"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> நா முன்பு"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டுக்கு முன்பு"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# நிமிடத்திற்கு முன்பு}other{# நிமிடங்களுக்கு முன்பு}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# மணிநேரத்திற்கு முன்பு}other{# மணிநேரத்திற்கு முன்பு}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# நாளுக்கு முன்பு}other{# நாட்களுக்கு முன்பு}}"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9495a96f6db6..c2be30e11c11 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"కాలర్ ID ఆటోమేటిక్‌లపై పరిమితి ఉంటుంది. తర్వాత కాల్: పరిమితి లేదు"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"కాలర్ ID ఆటోమేటిక్‌లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి ఉంటుంది"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID ఆటోమేటిక్‌లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి లేదు"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. APK అమరిక చెకింగ్ విఫలమైంది. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్‌ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్‌తో అప్లికేషన్‌ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ను చూడండి"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. ELF అమరిక చెకింగ్ విఫలమైంది. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్‌ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్‌తో అప్లికేషన్‌ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ను చూడండి"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. APK, ELF అమరిక చెకప్ దశలు విఫలమయ్యాయి. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్‌ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్‌తో అప్లికేషన్‌ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; ను చూడండి"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"సేవ కేటాయించబడలేదు."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్‌ను మార్చలేరు."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"డేటాను <xliff:g id="CARRIERDISPLAY">%s</xliff:g>కు స్విచ్ చేశారు"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>గంటలో"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>రోజులో"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరంలో"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>నిమిషం క్రితం"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>గంట క్రితం"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>రోజు క్రితం"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>సం క్రితం"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>నిమిషం"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>గంట"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>రోజు"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరం"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>నిమిషాలలో"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>గంటలో"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>రోజులో"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరంలో"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>నిమిషాల క్రితం"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>గంట క్రితం"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>రోజు క్రితం"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరం క్రితం"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# నిమిషం క్రితం}other{# నిమిషాల క్రితం}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# గంట క్రితం}other{# గంటల క్రితం}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# రోజు క్రితం}other{# రోజుల క్రితం}}"</string>
@@ -2101,7 +2114,7 @@
<string name="autofill_save_no" msgid="9212826374207023544">"వద్దు, ధన్యవాదాలు"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ఇప్పుడు కాదు"</string>
<string name="autofill_save_never" msgid="6821841919831402526">"ఎప్పుడూ వద్దు"</string>
- <string name="autofill_update_yes" msgid="4608662968996874445">"అప్‌డేట్ చేయి"</string>
+ <string name="autofill_update_yes" msgid="4608662968996874445">"అప్‌డేట్ చేయండి"</string>
<string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించండి"</string>
<string name="autofill_save_type_password" msgid="5624528786144539944">"పాస్‌వర్డ్"</string>
<string name="autofill_save_type_address" msgid="3111006395818252885">"అడ్రస్‌"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 86931c07b94c..7e70a3b573d8 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นถูกจำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ถูกจำกัด"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง APK คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่งของ APK และ ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ไม่มีการนำเสนอบริการ"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"คุณไม่สามารถเปลี่ยนการตั้งค่าหมายเลขผู้โทร"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"เปลี่ยนไปใช้อินเทอร์เน็ตมือถือของ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> แล้ว"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ใน <xliff:g id="COUNT">%d</xliff:g> ชม."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ใน <xliff:g id="COUNT">%d</xliff:g> วัน"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ใน <xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> นาทีที่ผ่านมา"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ชม. ที่ผ่านมา"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> วันที่ผ่านมา"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ปีที่ผ่านมา"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> นาที"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ชม."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> วัน"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ใน <xliff:g id="COUNT">%d</xliff:g> นาที"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"ใน <xliff:g id="COUNT">%d</xliff:g> ชม."</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"ใน <xliff:g id="COUNT">%d</xliff:g> วัน"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"ใน <xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> นาทีที่ผ่านมา"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ชม. ที่ผ่านมา"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> วันที่ผ่านมา"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ปีที่ผ่านมา"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# นาทีที่ผ่านมา}other{# นาทีที่ผ่านมา}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ชั่วโมงที่ผ่านมา}other{# ชั่วโมงที่ผ่านมา}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# วันที่ผ่านมา}other{# วันที่ผ่านมา}}"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 461050e1191b..6fd93bc5823c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nade-default sa pinaghihigpitan ang Caller ID. Susunod na tawag: hindi pinaghihigpitan"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Naka-default sa hindi pinaghihigpitan ang Caller ID. Susunod na tawag: Pinaghihigpitan"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Naka-default na hindi pinaghihigpitan ang Caller ID. Susunod na tawag: Hindi pinaghihigpitan"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang alignment check sa APK. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang alignment check sa ELF. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang mga alignment check sa APK at ELF. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Hindi naprobisyon ang serbisyo."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Hindi mo mababago ang setting ng caller ID."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Nailipat sa <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ang data"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"sa <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"sa <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"sa <xliff:g id="COUNT">%d</xliff:g>y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m nakalipas"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h nakalipas"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d nakalipas"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y nakalipas"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"sa <xliff:g id="COUNT">%d</xliff:g> (na) min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"sa loob ng <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"sa <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"sa <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ang nakalipas"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ang nakalipas"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ang nakalipas"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ang nakalipas"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto ang nakalipas}one{# minuto ang nakalipas}other{# na minuto ang nakalipas}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# oras ang nakalipas}one{# oras ang nakalipas}other{# na oras ang nakalipas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# araw ang nakalipas}one{# araw ang nakalipas}other{# na araw ang nakalipas}}"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bc90fe1ce1e4..16915b5b43f7 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Arayan kimliği varsayılanları kısıtlanmıştır. Sonraki çağrı: Kısıtlanmamış"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmış"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmamış"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu uygulama 16 KB ile uyumlu değil. APK uyumluluk kontrolü başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; adresini ziyaret edin."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu uygulama 16 KB ile uyumlu değil. ELF uyumluluk kontrolü başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; adresini ziyaret edin."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu uygulama 16 KB ile uyumlu değil. APK ve ELF uyumluluk kontrolleri başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; adresini ziyaret edin."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Hizmet sağlanamadı."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Arayanın kimliği ayarını değiştiremezsiniz."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Veriler şuraya aktarıldı: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> saat içinde"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> yıl içinde"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> dk. önce"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> sa. önce"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> gün önce"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> yıl önce"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> dk."</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> sa."</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> gün"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> yıl"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> dk. içinde"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> sa. içinde"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> yıl içinde"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> dk. önce"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> sa. önce"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> gün önce"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> yıl önce"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dakika önce}other{# dakika önce}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat önce}other{# saat önce}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün önce}other{# gün önce}}"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8b5a32101312..dcf15a0ff01c 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ідентиф. абонента за умовч. обмеж. Наст. дзвінок: не обмеж."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: обмеж."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: не обмежений"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання APK. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання ELF. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання APK і ELF. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Службу не ініціалізовано."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ви не можете змінювати налаштування ідентифікатора абонента."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобільний Інтернет переключено на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> год"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> р."</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> хв тому"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> год тому"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> дн. тому"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> р. тому"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> хв"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> дн."</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> р."</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"через <xliff:g id="COUNT">%d</xliff:g> хв"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"через <xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"через <xliff:g id="COUNT">%d</xliff:g> р."</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> хв тому"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> год тому"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> дн. тому"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> р. тому"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвилину тому}one{# хвилину тому}few{# хвилини тому}many{# хвилин тому}other{# хвилини тому}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# годину тому}one{# годину тому}few{# години тому}many{# годин тому}other{# години тому}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день тому}one{# день тому}few{# дні тому}many{# днів тому}other{# дня тому}}"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ae813281c6ed..8c75e818fb97 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"‏کالر ID کی ڈیفالٹ ترتیب محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"‏کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: محدود کردہ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"‏کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫ELF الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK اور ELF الائنمنٹ چیکس ناکام ہو گئے۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سروس فراہم نہیں کی گئی۔"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"‏آپ کالر ID کی ترتیبات تبدیل نہیں کر سکتے ہیں۔"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ڈیٹا <xliff:g id="CARRIERDISPLAY">%s</xliff:g> پر سوئچ کیا گیا"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> گھنٹے میں"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> دن میں"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> سال میں"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ قبل"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ قبل"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"‫<xliff:g id="COUNT">%d</xliff:g> دن قبل"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"‫<xliff:g id="COUNT">%d</xliff:g> سال قبل"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> دن"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سال"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ میں"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹے میں"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"‫<xliff:g id="COUNT">%d</xliff:g> دن میں"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"‫<xliff:g id="COUNT">%d</xliff:g> سال میں"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ قبل"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ قبل"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"‫<xliff:g id="COUNT">%d</xliff:g> دن قبل"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"‫<xliff:g id="COUNT">%d</xliff:g> سال قبل"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# منٹ پہلے}other{# منٹ پہلے}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# گھنٹہ پہلے}other{# گھنٹے پہلے}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# دن پہلے}other{# دن پہلے}}"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 54b51d979022..89e2e6859137 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklangan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklangan"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu ilova 16 KB sahifa hajmi bilan mos emas. APK fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu ilova 16 KB sahifa hajmi bilan mos emas. ELF fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu ilova 16 KB sahifa hajmi bilan mos emas. APK va ELF fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xizmat ishalamaydi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Qo‘ng‘iroq qiluvchining ID raqami sozlamasini o‘zgartirib bo‘lmaydi."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Internet <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoriga almashtirildi"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> soatdan keyin"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> kundan keyin"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> yildan keyin"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> daq oldin"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> soat oldin"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> kun oldin"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> yil oldin"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>daq"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>st"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> k"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yil"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> daqiqadan keyin"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> soatdan keyin"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> kundan keyin"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> yildan keyin"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> daqiqa oldin"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> soat oldin"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> kun oldin"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> yil oldin"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# daqiqa oldin}other{# daqiqa oldin}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# soat oldin}other{# soat oldin}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# kun oldin}other{# kun oldin}}"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9dc1a30455a8..ad0f42d2b868 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Số gọi đến mặc định thành bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Bị giới hạn"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp APK. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp ELF. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp APK và tệp ELF. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dịch vụ không được cấp phép."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Bạn không thể thay đổi cài đặt ID người gọi."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Đã chuyển dữ liệu sang <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> giờ nữa"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ngày nữa"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> năm nữa"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> phút trước"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> giờ trước"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ngày trước"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> năm trước"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> phút"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> giờ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ngày"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> năm"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> phút nữa"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> giờ nữa"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ngày nữa"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> năm nữa"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> phút trước"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> giờ trước"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ngày trước"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> năm trước"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# phút trước}other{# phút trước}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# giờ trước}other{# giờ trước}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ngày trước}other{# ngày trước}}"</string>
diff --git a/core/res/res/values-w192dp/dimens_material.xml b/core/res/res/values-w192dp/dimens_material.xml
index 797bf5a4c717..a11eb7f96f45 100644
--- a/core/res/res/values-w192dp/dimens_material.xml
+++ b/core/res/res/values-w192dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">7.99dp</dimen>
<dimen name="screen_percentage_05">9.6dp</dimen>
+ <dimen name="screen_percentage_052">9.98dp</dimen>
<dimen name="screen_percentage_10">19.2dp</dimen>
+ <dimen name="screen_percentage_12">23.04dp</dimen>
<dimen name="screen_percentage_15">28.8dp</dimen>
+ <dimen name="screen_percentage_3646">69.99dp</dimen>
</resources>
diff --git a/core/res/res/values-w195dp/dimens_material.xml b/core/res/res/values-w195dp/dimens_material.xml
index 7f3ad29f5505..346066f27349 100644
--- a/core/res/res/values-w195dp/dimens_material.xml
+++ b/core/res/res/values-w195dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.11dp</dimen>
<dimen name="screen_percentage_05">9.75dp</dimen>
+ <dimen name="screen_percentage_052">10.14dp</dimen>
<dimen name="screen_percentage_10">19.5dp</dimen>
+ <dimen name="screen_percentage_12">23.4dp</dimen>
<dimen name="screen_percentage_15">29.25dp</dimen>
+ <dimen name="screen_percentage_3646">71.09dp</dimen>
</resources>
diff --git a/core/res/res/values-w198dp/dimens_material.xml b/core/res/res/values-w198dp/dimens_material.xml
index a8aed2541c57..4c88f0520a71 100644
--- a/core/res/res/values-w198dp/dimens_material.xml
+++ b/core/res/res/values-w198dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.24dp</dimen>
<dimen name="screen_percentage_05">9.9dp</dimen>
+ <dimen name="screen_percentage_052">10.3dp</dimen>
<dimen name="screen_percentage_10">19.8dp</dimen>
+ <dimen name="screen_percentage_12">23.76dp</dimen>
<dimen name="screen_percentage_15">29.7dp</dimen>
+ <dimen name="screen_percentage_3646">72.1dp</dimen>
</resources>
diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml
index c07d5c47e2e1..54bb0c9c292e 100644
--- a/core/res/res/values-w204dp-round-watch/dimens_material.xml
+++ b/core/res/res/values-w204dp-round-watch/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.48dp</dimen>
<dimen name="screen_percentage_05">10.2dp</dimen>
+ <dimen name="screen_percentage_052">10.61dp</dimen>
<dimen name="screen_percentage_10">20.4dp</dimen>
+ <dimen name="screen_percentage_12">24.48dp</dimen>
<dimen name="screen_percentage_15">30.6dp</dimen>
+ <dimen name="screen_percentage_3646">74.42dp</dimen>
</resources>
diff --git a/core/res/res/values-w205dp/dimens_material.xml b/core/res/res/values-w205dp/dimens_material.xml
index 94907ee51dcc..60f65bb5863a 100644
--- a/core/res/res/values-w205dp/dimens_material.xml
+++ b/core/res/res/values-w205dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.52dp</dimen>
<dimen name="screen_percentage_05">10.25dp</dimen>
+ <dimen name="screen_percentage_052">10.66dp</dimen>
<dimen name="screen_percentage_10">20.5dp</dimen>
+ <dimen name="screen_percentage_12">24.6dp</dimen>
<dimen name="screen_percentage_15">30.75dp</dimen>
+ <dimen name="screen_percentage_3646">74.78dp</dimen>
</resources>
diff --git a/core/res/res/values-w208dp/dimens_material.xml b/core/res/res/values-w208dp/dimens_material.xml
index 069eeb0ad753..7f4ccd9d15cb 100644
--- a/core/res/res/values-w208dp/dimens_material.xml
+++ b/core/res/res/values-w208dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.65dp</dimen>
<dimen name="screen_percentage_05">10.4dp</dimen>
+ <dimen name="screen_percentage_052">10.82dp</dimen>
<dimen name="screen_percentage_10">20.8dp</dimen>
+ <dimen name="screen_percentage_12">24.96dp</dimen>
<dimen name="screen_percentage_15">31.2dp</dimen>
+ <dimen name="screen_percentage_3646">75.65dp</dimen>
</resources>
diff --git a/core/res/res/values-w210dp-round-watch/dimens_material.xml b/core/res/res/values-w210dp-round-watch/dimens_material.xml
index 79acf84b7e3f..ca0889e2c73b 100644
--- a/core/res/res/values-w210dp-round-watch/dimens_material.xml
+++ b/core/res/res/values-w210dp-round-watch/dimens_material.xml
@@ -14,6 +14,14 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.73dp</dimen>
+ <dimen name="screen_percentage_05">10.5dp</dimen>
+ <dimen name="screen_percentage_052">10.92dp</dimen>
+ <dimen name="screen_percentage_10">21dp</dimen>
+ <dimen name="screen_percentage_12">25.2dp</dimen>
+ <dimen name="screen_percentage_15">31.5dp</dimen>
+ <dimen name="screen_percentage_3646">76.57dp</dimen>
+
<dimen name="text_size_display_4_material">80sp</dimen>
<dimen name="text_size_display_3_material">50sp</dimen>
<dimen name="text_size_display_2_material">40sp</dimen>
diff --git a/core/res/res/values-w211dp/dimens_material.xml b/core/res/res/values-w211dp/dimens_material.xml
index bd7ca9aa9164..c483e455c5c3 100644
--- a/core/res/res/values-w211dp/dimens_material.xml
+++ b/core/res/res/values-w211dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.77dp</dimen>
<dimen name="screen_percentage_05">10.55dp</dimen>
+ <dimen name="screen_percentage_052">10.97dp</dimen>
<dimen name="screen_percentage_10">21.1dp</dimen>
+ <dimen name="screen_percentage_12">25.32dp</dimen>
<dimen name="screen_percentage_15">31.65dp</dimen>
+ <dimen name="screen_percentage_3646">76.93dp</dimen>
</resources>
diff --git a/core/res/res/values-w213dp/dimens_material.xml b/core/res/res/values-w213dp/dimens_material.xml
index 8a4e3a08ea13..093c298e1ac9 100644
--- a/core/res/res/values-w213dp/dimens_material.xml
+++ b/core/res/res/values-w213dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">8.85dp</dimen>
<dimen name="screen_percentage_05">10.65dp</dimen>
+ <dimen name="screen_percentage_052">11.07dp</dimen>
<dimen name="screen_percentage_10">21.3dp</dimen>
+ <dimen name="screen_percentage_12">25.56dp</dimen>
<dimen name="screen_percentage_15">31.95dp</dimen>
+ <dimen name="screen_percentage_3646">77.66dp</dimen>
</resources>
diff --git a/services/tests/security/intrusiondetection/res/xml/device_admin.xml b/core/res/res/values-w216dp/dimens_material.xml
index f8cd8f0b9b44..71dbf727480e 100644
--- a/services/tests/security/intrusiondetection/res/xml/device_admin.xml
+++ b/core/res/res/values-w216dp/dimens_material.xml
@@ -13,6 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-</device-admin>
+<resources>
+ <dimen name="screen_percentage_0416">8.99dp</dimen>
+ <dimen name="screen_percentage_05">10.8dp</dimen>
+ <dimen name="screen_percentage_052">11.23dp</dimen>
+ <dimen name="screen_percentage_10">21.6dp</dimen>
+ <dimen name="screen_percentage_12">25.92dp</dimen>
+ <dimen name="screen_percentage_15">32.4dp</dimen>
+ <dimen name="screen_percentage_3646">78.77dp</dimen>
+</resources>
diff --git a/core/res/res/values-w225dp/dimens_material.xml b/core/res/res/values-w225dp/dimens_material.xml
index aa822a32c975..6df34a5bb0ea 100644
--- a/core/res/res/values-w225dp/dimens_material.xml
+++ b/core/res/res/values-w225dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">9.36dp</dimen>
<dimen name="screen_percentage_05">11.25dp</dimen>
+ <dimen name="screen_percentage_052">11.7dp</dimen>
<dimen name="screen_percentage_10">22.5dp</dimen>
+ <dimen name="screen_percentage_12">27dp</dimen>
<dimen name="screen_percentage_15">33.75dp</dimen>
+ <dimen name="screen_percentage_3646">82.46dp</dimen>
</resources>
diff --git a/core/res/res/values-w227dp/dimens_material.xml b/core/res/res/values-w227dp/dimens_material.xml
index eb4df8a225d6..bbf4924ccc23 100644
--- a/core/res/res/values-w227dp/dimens_material.xml
+++ b/core/res/res/values-w227dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">9.44dp</dimen>
<dimen name="screen_percentage_05">11.35dp</dimen>
+ <dimen name="screen_percentage_052">11.8dp</dimen>
<dimen name="screen_percentage_10">22.7dp</dimen>
+ <dimen name="screen_percentage_12">27.24dp</dimen>
<dimen name="screen_percentage_15">34.05dp</dimen>
+ <dimen name="screen_percentage_3646">83.19dp</dimen>
</resources>
diff --git a/core/res/res/values-w228dp/dimens_material.xml b/core/res/res/values-w228dp/dimens_material.xml
index a2009754d95b..24bbb4c219ba 100644
--- a/core/res/res/values-w228dp/dimens_material.xml
+++ b/core/res/res/values-w228dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">9.48dp</dimen>
<dimen name="screen_percentage_05">11.4dp</dimen>
+ <dimen name="screen_percentage_052">11.86dp</dimen>
<dimen name="screen_percentage_10">22.8dp</dimen>
+ <dimen name="screen_percentage_12">27.36dp</dimen>
<dimen name="screen_percentage_15">34.2dp</dimen>
+ <dimen name="screen_percentage_3646">83.55dp</dimen>
</resources>
diff --git a/core/res/res/values-w240dp/dimens_material.xml b/core/res/res/values-w240dp/dimens_material.xml
index a4b58fa95c50..bd26c8bf84df 100644
--- a/core/res/res/values-w240dp/dimens_material.xml
+++ b/core/res/res/values-w240dp/dimens_material.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
<resources>
+ <dimen name="screen_percentage_0416">9.98dp</dimen>
<dimen name="screen_percentage_05">12dp</dimen>
+ <dimen name="screen_percentage_052">12.48dp</dimen>
<dimen name="screen_percentage_10">24dp</dimen>
+ <dimen name="screen_percentage_12">28.8dp</dimen>
<dimen name="screen_percentage_15">36dp</dimen>
+ <dimen name="screen_percentage_3646">87.5dp</dimen>
</resources>
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
deleted file mode 100644
index 4bc2a66fa206..000000000000
--- a/core/res/res/values-watch-v36/colors.xml
+++ /dev/null
@@ -1,18 +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.
- -->
-<!-- TODO(b/372524566): update color token's value to match material3 design. -->
-<resources>
-</resources> \ No newline at end of file
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
deleted file mode 100644
index 7232786d92c9..000000000000
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ /dev/null
@@ -1,39 +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.
- -->
-<resources>
- <!-- values for material3 button -->
- <dimen name="btn_material_width">172dp</dimen>
- <dimen name="btn_material_height">52dp</dimen>
- <dimen name="btn_horizontal_edge_padding">14dp</dimen>
- <dimen name="btn_drawable_padding">6dp</dimen>
- <dimen name="btn_lineHeight">18sp</dimen>
- <dimen name="btn_textSize">15sp</dimen>
-
- <!-- values for material3 AlertDialog -->
- <dimen name="dialog_btn_negative_width">60dp</dimen>
- <dimen name="dialog_btn_negative_height">60dp</dimen>
- <dimen name="dialog_btn_confirm_width">62dp</dimen>
- <dimen name="dialog_btn_confirm_height">60dp</dimen>
-
- <!-- Opacity factor for disabled material3 widget -->
- <dimen name="disabled_alpha_device_default">0.12</dimen>
- <dimen name="primary_content_alpha_device_default">0.38</dimen>
-
- <!-- values for material3 progress bar(progress indicator) -->
- <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item>
- <dimen name="progressbar_thickness">8dp</dimen>
- <dimen name="progressbar_elevation">0.1dp</dimen>
-</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
deleted file mode 100644
index 6e5ef68a70c3..000000000000
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<resources>
- <!-- Button Styles -->
- <!-- Material Button - Filled (primary colored) -->
- <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button.WearMaterial3">
- <item name="android:background">@drawable/btn_background_material_filled</item>
- <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
- </style>
-
- <!-- Material Button - Filled Tonal (Override system default button styles) -->
- <style name="Widget.DeviceDefault.Button.WearMaterial3">
- <item name="background">@drawable/btn_background_material_filled_tonal</item>
- <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
- <item name="minHeight">@dimen/btn_material_height</item>
- <item name="maxWidth">@dimen/btn_material_width</item>
- <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
- <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
- <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
- <item name="android:maxLines">2</item>
- <item name="android:ellipsize">end</item>
- <item name="android:breakStrategy">simple</item>
- <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
- <item name="focusable">true</item>
- <item name="clickable">true</item>
- <item name="gravity">center_vertical</item>
- </style>
-
- <!-- Material Button - Outlined -->
- <style name="Widget.DeviceDefault.Button.Outlined" parent="Widget.DeviceDefault.Button.WearMaterial3">
- <item name="android:background">@drawable/btn_background_material_outlined</item>
- </style>
-
- <!-- Material Button - Text -->
- <style name="Widget.DeviceDefault.Button.Text" parent="Widget.DeviceDefault.Button.WearMaterial3">
- <item name="android:background">@drawable/btn_background_material_text</item>
- </style>
-
- <!-- Text Styles -->
- <!-- TextAppearance for Material Button - Filled -->
- <style name="TextAppearance.Widget.Button.Material.Filled">
- <item name="textColor">@color/btn_material_filled_content_color</item>
- </style>
-
- <!-- TextAppearance for Material Button - Filled Tonal -->
- <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
- <item name="android:fontFamily">font-family-flex-device-default</item>
- <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
- <item name="textSize">@dimen/btn_textSize</item>
- <item name="textColor">@color/btn_material_filled_tonal_content_color</item>
- <item name="lineHeight">@dimen/btn_lineHeight</item>
- </style>
-
- <!-- AlertDialog Styles -->
- <style name="AlertDialog.DeviceDefault.WearMaterial3">
- <item name="layout">@layout/alert_dialog_wear_material3</item>
- </style>
-
- <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button">
- <item name="android:textSize">0sp</item>
- <item name="android:gravity">center</item>
- <item name="android:paddingStart">0dp</item>
- <item name="android:paddingEnd">0dp</item>
- <item name="android:drawablePadding">0dp</item>
- </style>
-
- <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm">
- <!-- Use a ImageView as background -->
- <item name="background">@android:color/transparent</item>
- <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
- <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
- </style>
-
- <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative">
- <item name="background">@drawable/dialog_alert_button_negative</item>
- <item name="minWidth">@dimen/dialog_btn_negative_width</item>
- <item name="minHeight">@dimen/dialog_btn_negative_height</item>
- <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
- <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
- </style>
-
- <!-- Wear Material3 Progress Bar style: progressed ring.-->
- <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3">
- <item name="indeterminateOnly">false</item>
- <item name="progressDrawable">@drawable/progress_ring_wear_material3</item>
- <item name="minHeight">@dimen/progress_bar_height</item>
- <item name="maxHeight">@dimen/progress_bar_height</item>
- <item name="mirrorForRtl">true</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 8e9693a5cfe9..73a7c094ac3f 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -69,4 +69,14 @@
<integer name="config_motionExpressiveSlowSpatialStiffness">200</integer>
<item name="config_motionExpressiveSlowEffectDamping" format="float" type="dimen">1.0</item>
<integer name="config_motionExpressiveSlowEffectStiffness">260</integer>
+
+ <!--
+ Material rounded corner configs
+ Values from https://carbon.googleplex.com/wear-m3/tokens/designSystems/70fbaa4f7722a3d1/tokenSets/4fa2518eaeaf65eb
+ -->
+ <dimen name="config_shapeCornerRadiusXsmall">4dp</dimen>
+ <dimen name="config_shapeCornerRadiusSmall">8dp</dimen>
+ <dimen name="config_shapeCornerRadiusMedium">18dp</dimen>
+ <dimen name="config_shapeCornerRadiusLarge">26dp</dimen>
+ <dimen name="config_shapeCornerRadiusXlarge">36dp</dimen>
</resources>
diff --git a/core/res/res/values-watch/styles_device_default.xml b/core/res/res/values-watch/styles_device_default.xml
deleted file mode 100644
index 8a2ce5da5985..000000000000
--- a/core/res/res/values-watch/styles_device_default.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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>
- <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
- <item name="maxLines">2</item>
- <item name="shadowRadius">0</item>
- <item name="ellipsize">end</item>
- <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
- </style>
- <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
- <item name="android:textSize">15sp</item>
- <item name="android:fontFamily">sans-serif</item>
- </style>
- <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
- <item name="android:textSize">16sp</item>
- <item name="android:fontFamily">google-sans</item>
- <item name="android:textStyle">bold</item>
- </style>
- <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
- <item name="android:textSize">16sp</item>
- <item name="android:fontFamily">google-sans-medium</item>
- </style>
- <style name="BaseErrorDialog.DeviceDefault" parent="AlertDialog.DeviceDefault">
- <item name="layout">@layout/watch_base_error_dialog</item>
- </style>
-</resources>
diff --git a/core/res/res/values-watch/styles_device_defaults.xml b/core/res/res/values-watch/styles_device_defaults.xml
new file mode 100644
index 000000000000..f3c85a96936f
--- /dev/null
+++ b/core/res/res/values-watch/styles_device_defaults.xml
@@ -0,0 +1,88 @@
+<?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>
+ <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
+ <item name="maxLines">2</item>
+ <item name="shadowRadius">0</item>
+ <item name="ellipsize">end</item>
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+ <item name="android:textSize">15sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ </style>
+ <style name="BaseErrorDialog.DeviceDefault" parent="AlertDialog.DeviceDefault">
+ <item name="layout">@layout/watch_base_error_dialog</item>
+ </style>
+
+ <!-- Button Styles -->
+ <!-- Material Button - Filled (primary colored) -->
+ <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button.WearMaterial3">
+ <item name="android:background">@drawable/btn_background_material_filled_watch</item>
+ <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
+ </style>
+
+ <!-- Material Button - Filled Tonal (Override system default button styles) -->
+ <style name="Widget.DeviceDefault.Button.WearMaterial3">
+ <item name="background">@drawable/btn_background_material_filled_tonal_watch</item>
+ <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
+ <item name="minHeight">@dimen/btn_material_height</item>
+ <item name="maxWidth">@dimen/btn_material_width</item>
+ <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
+ <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
+ <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:breakStrategy">simple</item>
+ <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
+ <item name="focusable">true</item>
+ <item name="clickable">true</item>
+ <item name="gravity">center_vertical</item>
+ </style>
+
+ <!-- Wear Material3 Button - Outlined -->
+ <style name="Widget.DeviceDefault.Button.Outlined" parent="Widget.DeviceDefault.Button.WearMaterial3">
+ <item name="android:background">@drawable/btn_background_material_outlined_watch</item>
+ </style>
+
+ <!-- Wear Material3 Button - Text -->
+ <style name="Widget.DeviceDefault.Button.Text" parent="Widget.DeviceDefault.Button.WearMaterial3">
+ <item name="android:background">@drawable/btn_background_material_text_watch</item>
+ </style>
+
+ <!-- Wear Material3 AlertDialog Styles -->
+ <style name="AlertDialog.DeviceDefault.WearMaterial3">
+ <item name="layout">@layout/alert_dialog_watch</item>
+ </style>
+
+ <!-- Wear Material3 Progress Bar style: progressed ring.-->
+ <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3">
+ <item name="indeterminateOnly">false</item>
+ <item name="progressDrawable">@drawable/progress_ring_watch</item>
+ <item name="minHeight">@dimen/progress_bar_height</item>
+ <item name="maxHeight">@dimen/progress_bar_height</item>
+ <item name="mirrorForRtl">true</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 7ac17595a278..60aec5342b4f 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -226,52 +226,6 @@ a similar way.
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorInverseOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorInversePrimary">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorInverseSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -376,8 +330,8 @@ a similar way.
<item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
</style>
- <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
- screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
+ <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
+ screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
<style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 98f1898b7fbd..120c08c75505 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"默认不显示本机号码,但在下一次通话中显示"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"默认显示本机号码,但在下一次通话中不显示"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"默认显示本机号码,在下一次通话中也显示"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"此应用不兼容 16 KB。APK 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"此应用不兼容 16 KB。ELF 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"此应用不兼容 16 KB。APK 和 ELF 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供服务。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"您无法更改来电显示设置。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"移动数据网络已切换至“<xliff:g id="CARRIERDISPLAY">%s</xliff:g>”"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>小时后"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>天后"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>年后"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分钟"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小时"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分钟后"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小时后"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天后"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年后"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分钟前}other{# 分钟前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小时前}other{# 小时前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index a544b44d0da3..9d6a2322fc48 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"預設不顯示來電號碼,但下一通電話則顯示。"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"預設顯示來電號碼,但下一通電話不顯示。"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示來電號碼,下一通電話也繼續顯示。"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"此應用程式不兼容 16KB。未能通過 APK 一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"此應用程式不兼容 16KB。未能通過電子商務記錄格式一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"此應用程式不兼容 16KB。未能通過 APK 和電子商務記錄格式一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"你無法更改來電顯示設定。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"流動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分鐘"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小時"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分鐘後"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 68158cea4559..7f22ab4a8d17 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"預設不顯示本機號碼,但下一通電話顯示。"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"預設顯示本機號碼,但下一通電話不顯示。"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示本機號碼,下一通電話也繼續顯示。"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"這個應用程式不支援 16 KB,APK 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"這個應用程式不支援 16 KB,ELF 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"這個應用程式不支援 16 KB,APK 和 ELF 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"無法提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"你無法變更來電顯示設定。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"行動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分鐘"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小時"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分鐘後"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9047a986c21c..0ac3e4af9554 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"i-ID yomshayeli ishintshela kokuvinjiwe. Ucingo olulandelayo: Aluvinjelwe"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Luvinjelwe"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Aluvinjelwe"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Le app ayihambelani ne-16 KB. Ukuhlolwa kokuqondanisa i-APK kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Le app ayihambelani ne-16 KB. Ukuhlolwa kokuqondanisa i-ELF kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Le app ayihambelani ne-16 KB. Ukuhlola ukuqondanisa i-APK ne-ELF kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Isevisi ayilungiselelwe."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ngeke ukwazi ukuguqul izilungiselelo zemininingwane yoshayayo."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Ushintshele idatha ku-<xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ngehora elingu-<xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ngosuku olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ngonyaka ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Umzuzu ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g> eledlule"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g> oludlule"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"Umzuzu o-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"Emzuzwini ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"Emahoreni angu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"Osukwini olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"onyakeni ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Umzuzu ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g> eledlule"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g> oludlule"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{umzuzu odlule #}one{imizuzu edlule #}other{imizuzu edlule #}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ihora elingu-# eledlule}one{amahora adlule angu-#}other{amahora adlule angu-#}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{usuku oludlule #}one{izinsuku ezedlule #}other{izinsuku ezedlule #}}"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 238aca556003..728c856f5855 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1217,178 +1217,6 @@
a value of 'true' will not override any 'false' value in its parent chain nor will
it prevent any 'false' in any of its children. -->
<attr name="forceDarkAllowed" format="boolean" />
-
- <!-- Dynamic Tokens -->
-
- <!-- @hide -->
- <attr name="materialColorBackground" format="color"/>
- <!-- @hide -->
- <attr name="materialColorControlActivated" format="color"/>
- <!-- @hide -->
- <attr name="materialColorControlHighlight" format="color"/>
- <!-- @hide -->
- <attr name="materialColorControlNormal" format="color"/>
- <!-- @hide -->
- <attr name="materialColorError" format="color"/>
- <!-- @hide -->
- <attr name="materialColorErrorContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorInverseOnSurface" format="color"/>
- <!-- @hide -->
- <attr name="materialColorInversePrimary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorInverseSurface" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnBackground" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnError" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnErrorContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnPrimary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnPrimaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSecondary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSecondaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSurface" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSurfaceVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnTertiary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnTertiaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOutline" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOutlineVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPaletteKeyColorNeutral" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPaletteKeyColorNeutralVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPaletteKeyColorPrimary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPaletteKeyColorSecondary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPaletteKeyColorTertiary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPrimary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPrimaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorScrim" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSecondary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSecondaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorShadow" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurface" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceBright" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceContainerHigh" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceContainerHighest" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceContainerLow" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceContainerLowest" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceDim" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceTint" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSurfaceVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTertiary" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTertiaryContainer" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTextHintInverse" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTextPrimaryInverse" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTextPrimaryInverseDisableOnly" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTextSecondaryAndTertiaryInverse" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTextSecondaryAndTertiaryInverseDisabled" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnPrimaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnPrimaryFixedVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSecondaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnSecondaryFixedVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnTertiaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorOnTertiaryFixedVariant" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPrimaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorPrimaryFixedDim" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSecondaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorSecondaryFixedDim" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTertiaryFixed" format="color"/>
- <!-- @hide -->
- <attr name="materialColorTertiaryFixedDim" format="color"/>
- <!-- @hide -->
- <attr name="customColorBrandA" format="color"/>
- <!-- @hide -->
- <attr name="customColorBrandB" format="color"/>
- <!-- @hide -->
- <attr name="customColorBrandC" format="color"/>
- <!-- @hide -->
- <attr name="customColorBrandD" format="color"/>
- <!-- @hide -->
- <attr name="customColorClockHour" format="color"/>
- <!-- @hide -->
- <attr name="customColorClockMinute" format="color"/>
- <!-- @hide -->
- <attr name="customColorClockSecond" format="color"/>
- <!-- @hide -->
- <attr name="customColorOnShadeActive" format="color"/>
- <!-- @hide -->
- <attr name="customColorOnShadeActiveVariant" format="color"/>
- <!-- @hide -->
- <attr name="customColorOnShadeInactive" format="color"/>
- <!-- @hide -->
- <attr name="customColorOnShadeInactiveVariant" format="color"/>
- <!-- @hide -->
- <attr name="customColorOnThemeApp" format="color"/>
- <!-- @hide -->
- <attr name="customColorOverviewBackground" format="color"/>
- <!-- @hide -->
- <attr name="customColorShadeActive" format="color"/>
- <!-- @hide -->
- <attr name="customColorShadeDisabled" format="color"/>
- <!-- @hide -->
- <attr name="customColorShadeInactive" format="color"/>
- <!-- @hide -->
- <attr name="customColorThemeApp" format="color"/>
- <!-- @hide -->
- <attr name="customColorThemeAppRing" format="color"/>
- <!-- @hide -->
- <attr name="customColorThemeNotif" format="color"/>
- <!-- @hide -->
- <attr name="customColorUnderSurface" format="color"/>
- <!-- @hide -->
- <attr name="customColorWeatherTemp" format="color"/>
- <!-- @hide -->
- <attr name="customColorWidgetBackground" format="color"/>
-
</declare-styleable>
<!-- **************************************************************** -->
@@ -4813,7 +4641,7 @@
role owner must opt into this behavior by using the property named by
{@link android.nfc.cardemulation.CardEmulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY }
in the <code>&lt;application&rt;</code> tag. -->
- <attr name="shareRolePriority" format="boolean"/>
+ <attr name="wantsRoleHolderPriority" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4841,7 +4669,7 @@
<!-- Whether the device should default to observe mode when this service is
default or in the foreground. -->
<attr name="shouldDefaultToObserveMode"/>
- <attr name="shareRolePriority"/>
+ <attr name="wantsRoleHolderPriority"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 00c59c6c0edc..8c6fd1dfc47e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2572,7 +2572,9 @@
against a development branch, in which case it will only work against
the development builds. -->
<attr name="minSdkVersion" format="integer|string" />
- <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+ <!-- This is the minimum SDK major and minor version (e.g. "36.1") that
+ the application requires. Verified independently of minSdkVersion.
+ @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
<attr name="minSdkVersionFull" format="string" />
<!-- This is the SDK version number that the application is targeting.
It is able to run on older versions (down to minSdkVersion), but
@@ -2890,6 +2892,17 @@
<attr name="name" />
</declare-styleable>
+ <!-- Private tag to declare the package name that the permissions of this package
+ is based on. Only used for packages installed in the system image. If
+ given, the permissions from the other package will be propagated into the
+ new package.
+
+ <p>This appears as a child tag of the root
+ {@link #AndroidManifest manifest} tag. -->
+ <declare-styleable name="AndroidManifestAdoptPermissions" parent="AndroidManifest">
+ <attr name="name" />
+ </declare-styleable>
+
<!-- The <code>processes</code> tag specifies the processes the application will run code in
and optionally characteristics of those processes. This tag is optional; if not
specified, components will simply run in the processes they specify. If supplied,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 390fbeaae494..3d023c3e1d11 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2372,7 +2372,7 @@
<string name="default_sms_application" translatable="false">com.android.messaging</string>
<!-- Flag indicating whether the current device supports "Ask every time" for sms-->
- <bool name="config_sms_ask_every_time_support">true</bool>
+ <bool name="config_sms_ask_every_time_support">false</bool>
<!-- Flag indicating whether the current device allows acknowledgement of SIM operation like
SM-PP or saving SMS to SIM can be done via the IMS interfaces.
@@ -2466,9 +2466,6 @@
<string name="config_systemCallStreaming" translatable="false"></string>
<!-- The name of the package that will hold the default retail demo role. -->
<string name="config_defaultRetailDemo" translatable="false"></string>
- <!-- The name of the package that will hold the default reserved for testing profile group
- exclusivity role. -->
- <string name="config_defaultReservedForTestingProfileGroupExclusivity" translatable="false">android.app.rolemultiuser.cts.app</string>
<!-- The component name of the wear service class that will be started by the system server. -->
<string name="config_wearServiceComponent" translatable="false"></string>
@@ -4228,13 +4225,12 @@
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
- <!-- Allow the gesture to double tap the power button to trigger a target action. -->
- <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Allow the gesture to double tap the power button twice to start the camera while the device
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
- <!-- Allow the gesture to double tap the power button twice to launch the wallet. -->
- <bool name="config_walletDoubleTapPowerGestureEnabled">true</bool>
+
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Default target action for double tap of the power button gesture.
0: Launch camera
1: Launch wallet -->
@@ -7213,10 +7209,6 @@
screen. -->
<bool name="config_dragToMaximizeInDesktopMode">false</bool>
- <!-- Whether showing the app handle is supported on this device.
- If config_isDesktopModeSupported, then this has no effect -->
- <bool name="config_enableAppHandle">false</bool>
-
<!-- Frame rate compatibility value for Wallpaper
FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption -->
<integer name="config_wallpaperFrameRateCompatibility">102</integer>
@@ -7276,4 +7268,9 @@
<!-- List of protected packages that require biometric authentication for modification
(Disable, force-stop or uninstalling updates). -->
<string-array name="config_biometric_protected_package_names" translatable="false" />
+
+ <!-- Package name of the on-device intelligent processor for vendor specific
+ features. Examples include the search functionality or the app
+ predictor. -->
+ <string name="config_systemVendorIntelligence" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/config_material.xml b/core/res/res/values/config_material.xml
index 6034f9c2daaf..648fe9078c40 100644
--- a/core/res/res/values/config_material.xml
+++ b/core/res/res/values/config_material.xml
@@ -75,4 +75,14 @@
<integer name="config_motionExpressiveSlowSpatialStiffness">200</integer>
<item name="config_motionExpressiveSlowEffectDamping" format="float" type="dimen">1.0</item>
<integer name="config_motionExpressiveSlowEffectStiffness">800</integer>
+
+ <!--
+ Material rounded corner configs
+ Values from https://carbon.googleplex.com/google-material-3/tokens/designSystems/20543ce18892f7d9/tokenSets/21c40db4e4f5af15
+ -->
+ <dimen name="config_shapeCornerRadiusXsmall">4dp</dimen>
+ <dimen name="config_shapeCornerRadiusSmall">8dp</dimen>
+ <dimen name="config_shapeCornerRadiusMedium">12dp</dimen>
+ <dimen name="config_shapeCornerRadiusLarge">16dp</dimen>
+ <dimen name="config_shapeCornerRadiusXlarge">28dp</dimen>
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index bb76b9fae8d7..666f1cf39fe3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -471,6 +471,12 @@
<string name="satellite_access_config_file" translatable="false"></string>
<java-symbol type="string" name="satellite_access_config_file" />
+ <!-- A string defines the NIDD (Non-IP Data Delivery) APN to be used for satellite attachment. For more on NIDD,
+ see 3GPP TS 29.542. This config is used for an NTN-only subscription, which requires activation before being used.
+ -->
+ <string name="config_satellite_nidd_apn_name" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_nidd_apn_name" />
+
<!-- Boolean indicating whether to enable MT SMS polling for NB IOT NTN. -->
<bool name="config_enabled_mt_sms_polling">true</bool>
<java-symbol type="bool" name="config_enabled_mt_sms_polling" />
@@ -496,4 +502,14 @@
not connected state. -->
<bool name="config_satellite_allow_check_message_in_not_connected">false</bool>
<java-symbol type="bool" name="config_satellite_allow_check_message_in_not_connected" />
+
+ <!-- Whether to allow TN scanning during satellite session. -->
+ <bool name="config_satellite_allow_tn_scanning_during_satellite_session">true</bool>
+ <java-symbol type="bool" name="config_satellite_allow_tn_scanning_during_satellite_session" />
+
+ <!-- List of integer tag Ids representing VZW satellite coverage. -->
+ <integer-array name="config_verizon_satellite_enabled_tagids">
+ </integer-array>
+ <java-symbol type="array" name="config_verizon_satellite_enabled_tagids" />
+
</resources>
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values/config_watch.xml
index 679dc709ec35..629a343f1280 100644
--- a/core/res/res/values-watch-v36/config.xml
+++ b/core/res/res/values/config_watch.xml
@@ -15,6 +15,8 @@
-->
<resources>
+ <!-- TODO(b/382103556): use predefined Material3 token -->
+ <!-- For Wear Material3 -->
<dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen>
<dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f53acbfac71d..51bd4cc6cc8a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -912,6 +912,8 @@
<dimen name="conversation_icon_size_badged">20dp</dimen>
<!-- size of the conversation avatar in an expanded group -->
<dimen name="conversation_avatar_size_group_expanded">@dimen/messaging_avatar_size</dimen>
+ <!-- size of the face pile icons (2025 redesign version) -->
+ <dimen name="notification_2025_face_pile_avatar_size">24dp</dimen>
<!-- size of the face pile icons -->
<dimen name="conversation_face_pile_avatar_size">32dp</dimen>
<!-- size of the face pile icons when the group is expanded -->
@@ -939,6 +941,18 @@
<!-- The size of the importance ring -->
<dimen name="importance_ring_size">20dp</dimen>
+ <!-- The spacing around the app icon badge shown next to the conversation icon -->
+ <dimen name="notification_2025_conversation_icon_badge_padding">2dp</dimen>
+
+ <!-- Top and start margin for the app icon badge shown next to the conversation icon, to align
+ it to the bottom end corner.
+ 40dp (conversation icon size) - 16dp (actual size of badge) - 2dp (badge padding) -->
+ <dimen name="notification_2025_conversation_icon_badge_position">22dp</dimen>
+
+ <!-- The size of the app icon badge shown next to the conversation icon, including its padding.
+ The actual size of the icon is 16dp, plus 2dp for each side for the padding. -->
+ <dimen name="notification_2025_conversation_icon_badge_size">20dp</dimen>
+
<!-- The top padding of the conversation icon container in the regular state-->
<dimen name="conversation_icon_container_top_padding">20dp</dimen>
diff --git a/core/res/res/values/dimens_watch.xml b/core/res/res/values/dimens_watch.xml
new file mode 100644
index 000000000000..2aae98715973
--- /dev/null
+++ b/core/res/res/values/dimens_watch.xml
@@ -0,0 +1,64 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- values for wear material3 button -->
+ <dimen name="btn_material_width">172dp</dimen>
+ <dimen name="btn_material_height">52dp</dimen>
+ <dimen name="btn_horizontal_edge_padding">14dp</dimen>
+ <dimen name="btn_drawable_padding">6dp</dimen>
+ <dimen name="btn_lineHeight">18sp</dimen>
+ <dimen name="btn_textSize">15sp</dimen>
+
+ <!-- values for wear material3 AlertDialog Title -->
+ <dimen name="alertDialog_material_line_height_title">18sp</dimen>
+ <dimen name="alertDialog_material_text_size_title">16sp</dimen>
+ <item name="alertDialog_material_letter_spacing_title" format="float" type="dimen">0.0125</item>
+ <dimen name="alertDialog_material_side_margin_title">@dimen/screen_percentage_12</dimen>
+
+ <!-- values for wear material3 AlertDialog Body -->
+ <dimen name="alertDialog_material_line_height_body_1">16sp</dimen>
+ <dimen name="alertDialog_material_text_size_body_1">14sp</dimen>
+ <item name="alertDialog_material_letter_spacing_body_1" format="float" type="dimen">0.0286</item>
+ <dimen name="alertDialog_material_side_margin_body">@dimen/screen_percentage_0416</dimen>
+
+ <!-- values for wear material3 AlertDialog -->
+ <dimen name="dialog_btn_negative_width">60dp</dimen>
+ <dimen name="dialog_btn_negative_height">60dp</dimen>
+ <dimen name="dialog_btn_confirm_width">62dp</dimen>
+ <dimen name="dialog_btn_confirm_height">60dp</dimen>
+ <dimen name="alertDialog_material_side_margin">@dimen/screen_percentage_052</dimen>
+ <dimen name="alertDialog_material_top_margin">@dimen/screen_percentage_10</dimen>
+ <dimen name="alertDialog_material_bottom_margin">@dimen/screen_percentage_3646</dimen>
+
+ <!-- Opacity factor for disabled wear material3 widget -->
+ <!-- Alpha transparency for widgets that set enablement/disablement programmatically
+ transparency is applied in the disabled state -->
+ <dimen name="disabled_alpha_device_default">0.12</dimen>
+ <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
+ <dimen name="primary_content_alpha_device_default">0.38</dimen>
+
+ <!-- values for wear material3 progress bar(progress indicator) -->
+ <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item>
+ <dimen name="progressbar_thickness">8dp</dimen>
+ <dimen name="progressbar_elevation">0.1dp</dimen>
+
+ <!-- Alpha transparency for wigets that set enablement/disablement programmatically
+ transparency is applied in the disabled state -->
+ <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>
+</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 90f1b8a7046b..e82992b91783 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -134,7 +134,7 @@
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
<public name="pageSizeCompat" />
<!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
- <public name="shareRolePriority"/>
+ <public name="wantsRoleHolderPriority"/>
<!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
<public name="minSdkVersionFull"/>
</staging-public-group>
@@ -151,9 +151,24 @@
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
@hide @SystemApi -->
<public name="config_systemDependencyInstaller" />
- <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED)
+ <!-- @hide @SystemApi -->
+ <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
@hide @SystemApi -->
- <public name="config_defaultReservedForTestingProfileGroupExclusivity" />
+ <public name="config_systemVendorIntelligence" />
+
+ <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+ @hide @SystemApi -->
+ <public name="config_defaultOnDeviceIntelligenceService"></public>
+
+ <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+ @hide @SystemApi -->
+ <public name="config_defaultOnDeviceSandboxedInferenceService"></public>
+
+ <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+ @hide @SystemApi -->
+ <public name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace"></public>
+
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
@@ -181,6 +196,16 @@
<public name="config_motionExpressiveSlowSpatialDamping"/>
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
<public name="config_motionExpressiveSlowEffectDamping"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusXsmall"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusSmall"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusMedium"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusLarge"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusXlarge"/>
</staging-public-group>
<staging-public-group type="color" first-id="0x01b20000">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index acc1ff8fb9db..326afba51fb2 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -44,6 +44,10 @@ easier.
<item name="textColor">@color/btn_colored_text_material</item>
</style>
<style name="Widget.DeviceDefault.Button.WearMaterial3"/>
+ <style name="Widget.DeviceDefault.Button.WearMaterial3.Filled"/>
+ <style name="Widget.DeviceDefault.Button.WearMaterial3.FilledTonal"/>
+ <style name="Widget.DeviceDefault.Button.WearMaterial3.Outlined"/>
+ <style name="Widget.DeviceDefault.Button.WearMaterial3.Text"/>
<style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" />
<style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
<style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
@@ -60,6 +64,7 @@ easier.
<style name="Widget.DeviceDefault.ProgressBar.Small" parent="Widget.Material.ProgressBar.Small"/>
<style name="Widget.DeviceDefault.ProgressBar.Small.Title" parent="Widget.Material.ProgressBar.Small.Title"/>
<style name="Widget.DeviceDefault.ProgressBar.Large" parent="Widget.Material.ProgressBar.Large"/>
+ <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3"/>
<style name="Widget.DeviceDefault.SeekBar" parent="Widget.Material.SeekBar"/>
<style name="Widget.DeviceDefault.RatingBar" parent="Widget.Material.RatingBar"/>
<style name="Widget.DeviceDefault.RatingBar.Indicator" parent="Widget.Material.RatingBar.Indicator"/>
@@ -430,6 +435,7 @@ easier.
<!-- AlertDialog Styles -->
<style name="AlertDialog.DeviceDefault" parent="AlertDialog.Material"/>
<style name="AlertDialog.DeviceDefault.Light" parent="AlertDialog.Material.Light"/>
+ <style name="AlertDialog.DeviceDefault.WearMaterial3"/>
<!-- Animation Styles -->
<style name="Animation.DeviceDefault.Activity" parent="Animation.Material.Activity"/>
diff --git a/core/res/res/values/styles_watch.xml b/core/res/res/values/styles_watch.xml
new file mode 100644
index 000000000000..3cf1f9b80fc4
--- /dev/null
+++ b/core/res/res/values/styles_watch.xml
@@ -0,0 +1,73 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- TextAppearance for Wear Material3 Button - Filled -->
+ <style name="TextAppearance.Widget.Button.Material.Filled">
+ <item name="textColor">@color/btn_material_filled_content_color_watch</item>
+ </style>
+
+ <!-- TextAppearance for Wear Material3 Button - Filled Tonal -->
+ <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">font-family-flex-device-default</item>
+ <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
+ <item name="textSize">@dimen/btn_textSize</item>
+ <item name="textColor">@color/btn_material_filled_tonal_content_color_watch</item>
+ <item name="lineHeight">@dimen/btn_lineHeight</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button">
+ <item name="android:textSize">0sp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:paddingStart">0dp</item>
+ <item name="android:paddingEnd">0dp</item>
+ <item name="android:drawablePadding">0dp</item>
+ </style>
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm">
+ <!-- Use a ImageView as background -->
+ <item name="background">@android:color/transparent</item>
+ <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
+ <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
+ </style>
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative">
+ <item name="background">@drawable/dialog_alert_button_negative_watch</item>
+ <item name="minWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="minHeight">@dimen/dialog_btn_negative_height</item>
+ <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
+ </style>
+
+ <!-- TextAppearance for wear material3 AlertDialog Body -->
+ <style name="TextAppearance.AlertDialog.Body1" parent="TextAppearance.Material.Body1">
+ <item name="android:fontFamily">font-family-flex-device-default</item>
+ <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 450, 'ROND' 100, 'GRAD' 0"</item>
+ <item name="android:textSize">@dimen/alertDialog_material_text_size_body_1</item>
+ <item name="android:lineHeight">@dimen/alertDialog_material_line_height_body_1</item>
+ <item name="android:letterSpacing">@dimen/alertDialog_material_letter_spacing_body_1</item>
+ </style>
+
+ <!-- TextAppearance for wear material3 AlertDialog Title -->
+ <style name="TextAppearance.AlertDialog.Title" parent="TextAppearance.Material.Title">
+ <item name="android:fontFamily">font-family-flex-device-default</item>
+ <item name="android:fontVariationSettings">"'wdth' 100, 'wght' 550, 'ROND' 100, 'GRAD' 0"</item>
+ <item name="android:textSize">@dimen/alertDialog_material_text_size_title</item>
+ <item name="android:lineHeight">@dimen/alertDialog_material_line_height_title</item>
+ <item name="android:letterSpacing">@dimen/alertDialog_material_letter_spacing_title</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:shadowRadius">0</item>
+ <item name="android:ellipsize">end</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2671ff90b35f..6c01994b3c93 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3161,9 +3161,8 @@
<java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
- <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
<java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
- <java-symbol type="bool" name="config_walletDoubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
<java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
<java-symbol type="bool" name="config_emergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
@@ -5446,133 +5445,51 @@
<java-symbol name="customColorWeatherTemp" type="color"/>
<java-symbol name="customColorWidgetBackground" type="color"/>
- <java-symbol type="attr" name="materialColorBackground"/>
- <java-symbol type="attr" name="materialColorControlActivated"/>
- <java-symbol type="attr" name="materialColorControlHighlight"/>
- <java-symbol type="attr" name="materialColorControlNormal"/>
- <java-symbol type="attr" name="materialColorError"/>
- <java-symbol type="attr" name="materialColorErrorContainer"/>
- <java-symbol type="attr" name="materialColorInverseOnSurface"/>
- <java-symbol type="attr" name="materialColorInversePrimary"/>
- <java-symbol type="attr" name="materialColorInverseSurface"/>
- <java-symbol type="attr" name="materialColorOnBackground"/>
- <java-symbol type="attr" name="materialColorOnError"/>
- <java-symbol type="attr" name="materialColorOnErrorContainer"/>
- <java-symbol type="attr" name="materialColorOnPrimary"/>
- <java-symbol type="attr" name="materialColorOnPrimaryContainer"/>
- <java-symbol type="attr" name="materialColorOnSecondary"/>
- <java-symbol type="attr" name="materialColorOnSecondaryContainer"/>
- <java-symbol type="attr" name="materialColorOnSurface"/>
- <java-symbol type="attr" name="materialColorOnSurfaceVariant"/>
- <java-symbol type="attr" name="materialColorOnTertiary"/>
- <java-symbol type="attr" name="materialColorOnTertiaryContainer"/>
- <java-symbol type="attr" name="materialColorOutline"/>
- <java-symbol type="attr" name="materialColorOutlineVariant"/>
- <java-symbol type="attr" name="materialColorPaletteKeyColorNeutral"/>
- <java-symbol type="attr" name="materialColorPaletteKeyColorNeutralVariant"/>
- <java-symbol type="attr" name="materialColorPaletteKeyColorPrimary"/>
- <java-symbol type="attr" name="materialColorPaletteKeyColorSecondary"/>
- <java-symbol type="attr" name="materialColorPaletteKeyColorTertiary"/>
- <java-symbol type="attr" name="materialColorPrimary"/>
- <java-symbol type="attr" name="materialColorPrimaryContainer"/>
- <java-symbol type="attr" name="materialColorScrim"/>
- <java-symbol type="attr" name="materialColorSecondary"/>
- <java-symbol type="attr" name="materialColorSecondaryContainer"/>
- <java-symbol type="attr" name="materialColorShadow"/>
- <java-symbol type="attr" name="materialColorSurface"/>
- <java-symbol type="attr" name="materialColorSurfaceBright"/>
- <java-symbol type="attr" name="materialColorSurfaceContainer"/>
- <java-symbol type="attr" name="materialColorSurfaceContainerHigh"/>
- <java-symbol type="attr" name="materialColorSurfaceContainerHighest"/>
- <java-symbol type="attr" name="materialColorSurfaceContainerLow"/>
- <java-symbol type="attr" name="materialColorSurfaceContainerLowest"/>
- <java-symbol type="attr" name="materialColorSurfaceDim"/>
- <java-symbol type="attr" name="materialColorSurfaceTint"/>
- <java-symbol type="attr" name="materialColorSurfaceVariant"/>
- <java-symbol type="attr" name="materialColorTertiary"/>
- <java-symbol type="attr" name="materialColorTertiaryContainer"/>
- <java-symbol type="attr" name="materialColorTextHintInverse"/>
- <java-symbol type="attr" name="materialColorTextPrimaryInverse"/>
- <java-symbol type="attr" name="materialColorTextPrimaryInverseDisableOnly"/>
- <java-symbol type="attr" name="materialColorTextSecondaryAndTertiaryInverse"/>
- <java-symbol type="attr" name="materialColorTextSecondaryAndTertiaryInverseDisabled"/>
- <java-symbol type="attr" name="materialColorOnPrimaryFixed"/>
- <java-symbol type="attr" name="materialColorOnPrimaryFixedVariant"/>
- <java-symbol type="attr" name="materialColorOnSecondaryFixed"/>
- <java-symbol type="attr" name="materialColorOnSecondaryFixedVariant"/>
- <java-symbol type="attr" name="materialColorOnTertiaryFixed"/>
- <java-symbol type="attr" name="materialColorOnTertiaryFixedVariant"/>
- <java-symbol type="attr" name="materialColorPrimaryFixed"/>
- <java-symbol type="attr" name="materialColorPrimaryFixedDim"/>
- <java-symbol type="attr" name="materialColorSecondaryFixed"/>
- <java-symbol type="attr" name="materialColorSecondaryFixedDim"/>
- <java-symbol type="attr" name="materialColorTertiaryFixed"/>
- <java-symbol type="attr" name="materialColorTertiaryFixedDim"/>
- <java-symbol type="attr" name="customColorBrandA"/>
- <java-symbol type="attr" name="customColorBrandB"/>
- <java-symbol type="attr" name="customColorBrandC"/>
- <java-symbol type="attr" name="customColorBrandD"/>
- <java-symbol type="attr" name="customColorClockHour"/>
- <java-symbol type="attr" name="customColorClockMinute"/>
- <java-symbol type="attr" name="customColorClockSecond"/>
- <java-symbol type="attr" name="customColorOnShadeActive"/>
- <java-symbol type="attr" name="customColorOnShadeActiveVariant"/>
- <java-symbol type="attr" name="customColorOnShadeInactive"/>
- <java-symbol type="attr" name="customColorOnShadeInactiveVariant"/>
- <java-symbol type="attr" name="customColorOnThemeApp"/>
- <java-symbol type="attr" name="customColorOverviewBackground"/>
- <java-symbol type="attr" name="customColorShadeActive"/>
- <java-symbol type="attr" name="customColorShadeDisabled"/>
- <java-symbol type="attr" name="customColorShadeInactive"/>
- <java-symbol type="attr" name="customColorThemeApp"/>
- <java-symbol type="attr" name="customColorThemeAppRing"/>
- <java-symbol type="attr" name="customColorThemeNotif"/>
- <java-symbol type="attr" name="customColorUnderSurface"/>
- <java-symbol type="attr" name="customColorWeatherTemp"/>
- <java-symbol type="attr" name="customColorWidgetBackground"/>
-
- <java-symbol name="system_widget_background_light" type="color"/>
- <java-symbol name="system_clock_hour_light" type="color"/>
- <java-symbol name="system_clock_minute_light" type="color"/>
- <java-symbol name="system_clock_second_light" type="color"/>
- <java-symbol name="system_theme_app_light" type="color"/>
- <java-symbol name="system_on_theme_app_light" type="color"/>
- <java-symbol name="system_theme_app_ring_light" type="color"/>
- <java-symbol name="system_theme_notif_light" type="color"/>
<java-symbol name="system_brand_a_light" type="color"/>
<java-symbol name="system_brand_b_light" type="color"/>
<java-symbol name="system_brand_c_light" type="color"/>
<java-symbol name="system_brand_d_light" type="color"/>
- <java-symbol name="system_under_surface_light" type="color"/>
- <java-symbol name="system_shade_active_light" type="color"/>
+ <java-symbol name="system_clock_hour_light" type="color"/>
+ <java-symbol name="system_clock_minute_light" type="color"/>
+ <java-symbol name="system_clock_second_light" type="color"/>
<java-symbol name="system_on_shade_active_light" type="color"/>
<java-symbol name="system_on_shade_active_variant_light" type="color"/>
- <java-symbol name="system_shade_inactive_light" type="color"/>
<java-symbol name="system_on_shade_inactive_light" type="color"/>
<java-symbol name="system_on_shade_inactive_variant_light" type="color"/>
- <java-symbol name="system_shade_disabled_light" type="color"/>
+ <java-symbol name="system_on_theme_app_light" type="color"/>
<java-symbol name="system_overview_background_light" type="color"/>
- <java-symbol name="system_widget_background_dark" type="color"/>
- <java-symbol name="system_clock_hour_dark" type="color"/>
- <java-symbol name="system_clock_minute_dark" type="color"/>
- <java-symbol name="system_clock_second_dark" type="color"/>
- <java-symbol name="system_theme_app_dark" type="color"/>
- <java-symbol name="system_on_theme_app_dark" type="color"/>
- <java-symbol name="system_theme_app_ring_dark" type="color"/>
- <java-symbol name="system_theme_notif_dark" type="color"/>
+ <java-symbol name="system_shade_active_light" type="color"/>
+ <java-symbol name="system_shade_disabled_light" type="color"/>
+ <java-symbol name="system_shade_inactive_light" type="color"/>
+ <java-symbol name="system_theme_app_light" type="color"/>
+ <java-symbol name="system_theme_app_ring_light" type="color"/>
+ <java-symbol name="system_theme_notif_light" type="color"/>
+ <java-symbol name="system_under_surface_light" type="color"/>
+ <java-symbol name="system_weather_temp_light" type="color"/>
+ <java-symbol name="system_widget_background_light" type="color"/>
+
<java-symbol name="system_brand_a_dark" type="color"/>
<java-symbol name="system_brand_b_dark" type="color"/>
<java-symbol name="system_brand_c_dark" type="color"/>
<java-symbol name="system_brand_d_dark" type="color"/>
- <java-symbol name="system_under_surface_dark" type="color"/>
- <java-symbol name="system_shade_active_dark" type="color"/>
+ <java-symbol name="system_clock_hour_dark" type="color"/>
+ <java-symbol name="system_clock_minute_dark" type="color"/>
+ <java-symbol name="system_clock_second_dark" type="color"/>
<java-symbol name="system_on_shade_active_dark" type="color"/>
<java-symbol name="system_on_shade_active_variant_dark" type="color"/>
- <java-symbol name="system_shade_inactive_dark" type="color"/>
<java-symbol name="system_on_shade_inactive_dark" type="color"/>
<java-symbol name="system_on_shade_inactive_variant_dark" type="color"/>
- <java-symbol name="system_shade_disabled_dark" type="color"/>
+ <java-symbol name="system_on_theme_app_dark" type="color"/>
<java-symbol name="system_overview_background_dark" type="color"/>
+ <java-symbol name="system_shade_active_dark" type="color"/>
+ <java-symbol name="system_shade_disabled_dark" type="color"/>
+ <java-symbol name="system_shade_inactive_dark" type="color"/>
+ <java-symbol name="system_theme_app_dark" type="color"/>
+ <java-symbol name="system_theme_app_ring_dark" type="color"/>
+ <java-symbol name="system_theme_notif_dark" type="color"/>
+ <java-symbol name="system_under_surface_dark" type="color"/>
+ <java-symbol name="system_weather_temp_dark" type="color"/>
+ <java-symbol name="system_widget_background_dark" type="color"/>
<java-symbol type="attr" name="actionModeUndoDrawable" />
<java-symbol type="attr" name="actionModeRedoDrawable" />
@@ -5764,9 +5681,6 @@
screen. -->
<java-symbol type="bool" name="config_dragToMaximizeInDesktopMode" />
- <!-- Whether showing the app handle is supported on this device -->
- <java-symbol type="bool" name="config_enableAppHandle" />
-
<!-- Frame rate compatibility value for Wallpaper -->
<java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
@@ -5878,4 +5792,12 @@
<!-- List of protected packages that require biometric authentication for modification -->
<java-symbol type="array" name="config_biometric_protected_package_names" />
+ <!-- Material shape spec config tokens -->
+ <java-symbol type="dimen" name="config_shapeCornerRadiusXsmall"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusSmall"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusMedium"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusLarge"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusXlarge"/>
+
+
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index d8346d87f624..6b3d4271a609 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -131,7 +131,7 @@ easier.
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.ProgressBar.Large.Inverse</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.RatingBar.Indicator</item>
@@ -238,91 +238,6 @@ easier.
<item name="textColorOnAccent">@color/system_on_primary_dark</item>
<item name="colorForeground">@color/foreground_device_default_dark</item>
<item name="colorForegroundInverse">@color/foreground_device_default_light</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -368,96 +283,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -502,96 +332,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -638,96 +383,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -773,96 +433,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -916,96 +491,11 @@ easier.
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -1050,96 +540,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -1183,96 +588,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1317,96 +637,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1467,96 +702,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1602,96 +752,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1735,96 +800,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1870,96 +850,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -2004,96 +899,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -2138,96 +948,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -2272,96 +997,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -2406,96 +1046,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -2540,96 +1095,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -2679,96 +1149,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2811,96 +1196,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2982,7 +1282,7 @@ easier.
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.Light.RatingBar.Indicator</item>
@@ -3086,91 +1386,6 @@ easier.
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -3215,96 +1430,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -3348,96 +1478,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -3482,96 +1527,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -3618,96 +1578,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -3753,96 +1628,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -3894,96 +1684,11 @@ easier.
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -4031,96 +1736,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -4167,96 +1787,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -4304,96 +1839,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -4427,91 +1877,6 @@ easier.
<item name="textColorOnAccent">@color/system_on_primary_dark</item>
<item name="colorForeground">@color/foreground_device_default_light</item>
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -4545,91 +1910,6 @@ easier.
<item name="textColorOnAccent">@color/system_on_primary_dark</item>
<item name="colorForeground">@color/foreground_device_default_light</item>
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -4677,96 +1957,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -4815,96 +2010,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -4951,96 +2061,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -5086,96 +2111,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -5220,96 +2160,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -5354,96 +2209,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -5486,96 +2256,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -5631,90 +2316,6 @@ easier.
<item name="colorListDivider">@color/list_divider_color_light</item>
<item name="opacityListDivider">@color/list_divider_opacity_device_default_light</item>
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light">
@@ -5745,96 +2346,12 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -5857,96 +2374,12 @@ easier.
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -5991,96 +2424,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -6114,91 +2462,6 @@ easier.
<!-- Dialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase">
@@ -6216,7 +2479,7 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -6267,96 +2530,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -6403,96 +2581,11 @@ easier.
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
+ <item name="colorProgressBackgroundNormal">@color/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -6570,91 +2663,6 @@ easier.
<item name="colorAccentPrimary">@color/accent_primary_device_default</item>
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="ThemeOverlay.DeviceDefault.Accent.Light">
@@ -6662,91 +2670,6 @@ easier.
<item name="colorAccentPrimary">@color/accent_primary_device_default</item>
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -6758,91 +2681,6 @@ easier.
<item name="colorAccentPrimary">@color/accent_primary_device_default</item>
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -6850,91 +2688,6 @@ easier.
<item name="colorBackgroundFloating">@color/background_device_default_light</item>
<item name="layout_gravity">center</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
-
- <item name="materialColorBackground">@color/system_background_light</item>
- <item name="materialColorControlActivated">@color/system_control_activated_light</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
- <item name="materialColorControlNormal">@color/system_control_normal_light</item>
- <item name="materialColorError">@color/system_error_light</item>
- <item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
- <item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorOutline">@color/system_outline_light</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
- <item name="materialColorPrimary">@color/system_primary_light</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
- <item name="materialColorScrim">@color/system_scrim_light</item>
- <item name="materialColorSecondary">@color/system_secondary_light</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
- <item name="materialColorShadow">@color/system_shadow_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_light</item>
- <item name="customColorBrandB">@color/system_brand_b_light</item>
- <item name="customColorBrandC">@color/system_brand_c_light</item>
- <item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -6953,91 +2706,6 @@ easier.
<item name="colorAccentPrimary">@color/system_accent1_100</item>
<item name="textColorPrimary">@color/system_neutral1_900</item>
<item name="textColorSecondary">@color/system_neutral2_700</item>
-
- <item name="materialColorBackground">@color/system_background_dark</item>
- <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
- <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
- <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
- <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
- <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
- <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
- <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
- <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
- <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
- <item name="materialColorPrimary">@color/system_primary_dark</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorScrim">@color/system_scrim_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorShadow">@color/system_shadow_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
- <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
- <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
- <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="customColorBrandA">@color/system_brand_a_dark</item>
- <item name="customColorBrandB">@color/system_brand_b_dark</item>
- <item name="customColorBrandC">@color/system_brand_c_dark</item>
- <item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
- <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
- <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
<item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml
index e735784ee5bb..17860ef6d9f2 100644
--- a/core/res/res/xml/bookmarks.xml
+++ b/core/res/res/xml/bookmarks.xml
@@ -20,14 +20,10 @@
Typical shortcuts (not necessarily defined here):
'b': Browser
- 'c': Contacts
+ 'p': Contacts
'e': Email
- 'g': GMail
- 'k': Calendar
+ 'c': Calendar
'm': Maps
- 'p': Music
- 's': SMS
- 't': Talk
'u': Calculator
'y': YouTube
-->
@@ -38,7 +34,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -46,21 +42,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 49425572b256..c67a0f9659f0 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -530,14 +530,25 @@ test_module_config {
}
test_module_config {
- name: "FrameworksCoreTests_internal_os_binder",
+ name: "FrameworksCoreTests_all_binder",
base: "FrameworksCoreTests",
test_suites: [
"automotive-tests",
"device-tests",
"device-platinum-tests",
],
- include_filters: ["com.android.internal.os.BinderDeathDispatcherTest"],
+ include_filters: [
+ "android.os.BinderProxyTest",
+ "android.os.BinderDeathRecipientTest",
+ "android.os.BinderFrozenStateChangeNotificationTest",
+ "android.os.BinderProxyCountingTest",
+ "android.os.BinderUncaughtExceptionHandlerTest",
+ "android.os.BinderThreadPriorityTest",
+ "android.os.BinderWorkSourceTest",
+ "android.os.ParcelNullabilityTest",
+ "android.os.ParcelTest",
+ "com.android.internal.os.BinderDeathDispatcherTest",
+ ],
exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
}
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
new file mode 100644
index 000000000000..3213abe13d8a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.app;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+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 org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+import java.time.InstantSource;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class NotificationManagerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private NotificationManagerWithMockService mNotificationManager;
+ private final FakeClock mClock = new FakeClock();
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mNotificationManager = new NotificationManagerWithMockService(mContext, mClock);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void notify_rapidUpdate_isThrottled() throws Exception {
+ Notification n = exampleNotification();
+
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mClock.advanceByMillis(5);
+ }
+
+ verify(mNotificationManager.mBackendService, atMost(30)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void notify_reasonableUpdate_isNotThrottled() throws Exception {
+ Notification n = exampleNotification();
+
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mClock.advanceByMillis(300);
+ }
+
+ verify(mNotificationManager.mBackendService, times(100)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void notify_rapidAdd_isNotThrottled() throws Exception {
+ Notification n = exampleNotification();
+
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(i, n);
+ mClock.advanceByMillis(5);
+ }
+
+ verify(mNotificationManager.mBackendService, times(100)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void notify_rapidAddAndCancel_isNotThrottled() throws Exception {
+ Notification n = exampleNotification();
+
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mNotificationManager.cancel(1);
+ mClock.advanceByMillis(5);
+ }
+
+ verify(mNotificationManager.mBackendService, times(100)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ }
+
+ private Notification exampleNotification() {
+ return new Notification.Builder(mContext, "channel")
+ .setSmallIcon(android.R.drawable.star_big_on)
+ .build();
+ }
+
+ private static class NotificationManagerWithMockService extends NotificationManager {
+
+ private final INotificationManager mBackendService;
+
+ NotificationManagerWithMockService(Context context, InstantSource clock) {
+ super(context, clock);
+ mBackendService = mock(INotificationManager.class);
+ }
+
+ @Override
+ public INotificationManager service() {
+ return mBackendService;
+ }
+ }
+
+ private static class FakeClock implements InstantSource {
+
+ private long mNowMillis = 441644400000L;
+
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(mNowMillis);
+ }
+
+ private void advanceByMillis(long millis) {
+ mNowMillis += millis;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 63e678d9ee53..9effeec23890 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -467,12 +467,25 @@ public class NotificationTest {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isTrue();
}
@Test
@EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_notOngoing() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+ .setColor(Color.WHITE)
+ .setColorized(true)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
public void testHasPromotableCharacteristics_wrongStyle() {
Notification n = new Notification.Builder(mContext, "test")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -480,6 +493,7 @@ public class NotificationTest {
.setContentTitle("TITLE")
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -491,6 +505,7 @@ public class NotificationTest {
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -503,6 +518,7 @@ public class NotificationTest {
.setStyle(new Notification.BigTextStyle())
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -515,6 +531,7 @@ public class NotificationTest {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setGroup("someGroup")
.setGroupSummary(true)
.build();
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 177c7f0b2f27..e9dfdd826572 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,7 +16,6 @@
package android.app;
-import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH;
@@ -711,7 +710,6 @@ public class PropertyInvalidatedCacheTests {
}
}
- @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
@Test
public void testCachingNulls() {
TestCache cache = new TestCache(new Args(MODULE_TEST)
@@ -740,5 +738,20 @@ public class PropertyInvalidatedCacheTests {
assertEquals(null, cache.query(30));
// The recompute is 4 because nulls were not cached.
assertEquals(4, cache.getRecomputeCount());
+
+ // Verify that the default is not to cache nulls.
+ cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).api("testCachingNulls"),
+ new TestQuery());
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
}
}
diff --git a/core/tests/coretests/src/android/app/QueuedWorkTest.java b/core/tests/coretests/src/android/app/QueuedWorkTest.java
index 230c9e86db6b..6bd9b6a1453f 100644
--- a/core/tests/coretests/src/android/app/QueuedWorkTest.java
+++ b/core/tests/coretests/src/android/app/QueuedWorkTest.java
@@ -163,18 +163,18 @@ public class QueuedWorkTest {
@Test
public void testHasPendingWork() {
- Semaphore releaser = new Semaphore(0);
- mQueuedWork.queue(
- () -> {
- try {
- releaser.acquire();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }, false);
+ final Semaphore releaser1 = new Semaphore(0);
+ final Semaphore releaser2 = new Semaphore(0);
+ mQueuedWork.queue(() -> releaser1.acquireUninterruptibly(), false);
+ mQueuedWork.queue(() -> releaser2.release(), false);
+ // Worker should be waiting for releaser1,
+ // and have pending work to release releaser2
assertThat(mQueuedWork.hasPendingWork()).isTrue();
- releaser.release();
- mQueuedWork.waitToFinish();
+
+ // Allow worker to get to releasing releaser2
+ releaser1.release();
+ releaser2.acquireUninterruptibly();
+ // If we got here then there is no pending work.
assertThat(mQueuedWork.hasPendingWork()).isFalse();
}
} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8d045f87063b..1f1000f2800d 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -49,6 +49,7 @@ import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.app.ResourcesManager;
+import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -79,6 +80,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.Display;
+import android.view.Surface;
import android.view.View;
import android.window.ActivityWindowInfo;
import android.window.WindowContextInfo;
@@ -302,6 +304,59 @@ public class ActivityThreadTest {
assertScreenScale(originalScale, app, originalAppConfig, originalAppMetrics);
}
+ @Test
+ public void testOverrideDisplayRotation() throws Exception {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final Application app = activity.getApplication();
+ final ActivityThread activityThread = activity.getActivityThread();
+ final IApplicationThread appThread = activityThread.getApplicationThread();
+ final Configuration originalAppConfig =
+ new Configuration(app.getResources().getConfiguration());
+ final int originalDisplayRotation = originalAppConfig.windowConfiguration
+ .getDisplayRotation();
+
+ final Configuration newConfig = new Configuration(originalAppConfig);
+ newConfig.seq = BASE_SEQ + 1;
+
+ int sandboxedDisplayRotation = (originalDisplayRotation + 1) % 4;
+ CompatibilityInfo.setOverrideDisplayRotation(sandboxedDisplayRotation);
+ try {
+ // Send process level config change.
+ ClientTransaction transaction = newTransaction(activityThread);
+ transaction.addTransactionItem(
+ new ConfigurationChangeItem(newConfig, DEVICE_ID_INVALID));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertDisplayRotation(sandboxedDisplayRotation, app);
+
+ sandboxedDisplayRotation = (sandboxedDisplayRotation + 1) % 4;
+ CompatibilityInfo.setOverrideDisplayRotation(sandboxedDisplayRotation);
+ // Send activity level config change.
+ newConfig.seq++;
+ transaction = newTransaction(activityThread);
+ transaction.addTransactionItem(new ActivityConfigurationChangeItem(
+ activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertDisplayRotation(sandboxedDisplayRotation, activity);
+
+ // Execute a local relaunch item with current scaled config (e.g. simulate recreate),
+ // the config should not change again.
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> activityThread.executeTransaction(
+ newRelaunchResumeTransaction(activity)));
+
+ assertDisplayRotation(sandboxedDisplayRotation, activity);
+ } finally {
+ CompatibilityInfo.setOverrideDisplayRotation(WindowConfiguration.ROTATION_UNDEFINED);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> restoreConfig(activityThread, originalAppConfig));
+ }
+ assertDisplayRotation(originalDisplayRotation, app);
+ }
+
private static void assertScreenScale(float scale, Context context,
Configuration origConfig, DisplayMetrics origMetrics) {
final int expectedDpi = (int) (origConfig.densityDpi * scale + .5f);
@@ -326,6 +381,12 @@ public class ActivityThreadTest {
assertEquals(expectedMaxBounds, currentConfig.windowConfiguration.getMaxBounds());
}
+ private static void assertDisplayRotation(@Surface.Rotation int expectedRotation,
+ Context context) {
+ final Configuration currentConfig = context.getResources().getConfiguration();
+ assertEquals(expectedRotation, currentConfig.windowConfiguration.getDisplayRotation());
+ }
+
@Test
public void testHandleActivityConfigurationChanged() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
index 6d2dd5355ff0..ff3abae29b4a 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -33,7 +33,6 @@ import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -62,7 +61,6 @@ import java.util.List;
import java.util.Set;
@Presubmit
-@RequiresFlagsEnabled(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
public class ApkLiteParseUtilsTest {
@Rule
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 6dad3b7b2ac4..51a43ac01d75 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -1010,6 +1010,7 @@ public class SQLiteRawStatementTest {
mDatabase.beginTransaction();
try {
mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
+ mDatabase.execSQL("INSERT INTO t1 (i, j) VALUES (2, 20)");
mDatabase.setTransactionSuccessful();
} finally {
mDatabase.endTransaction();
@@ -1017,13 +1018,58 @@ public class SQLiteRawStatementTest {
mDatabase.beginTransactionReadOnly();
try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
- s.step();
- s.getColumnText(5); // out-of-range column
+ assertTrue(s.step());
+ s.getColumnText(5); // out-of-range column: the range is [0,2).
fail("JNI exception not thrown");
} catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
// Passing case.
} finally {
mDatabase.endTransaction();
}
+
+ mDatabase.beginTransactionReadOnly();
+ try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+ // Do not step the statement. The column count will be zero.
+ s.getColumnText(5); // out-of-range column: never stepped.
+ fail("JNI exception not thrown");
+ } catch (SQLiteMisuseException e) {
+ // Passing case.
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ mDatabase.beginTransactionReadOnly();
+ try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+ // Do not step the statement. The column count will be zero.
+ s.getColumnText(0); // out-of-range column: never stepped.
+ fail("JNI exception not thrown");
+ } catch (SQLiteMisuseException e) {
+ // Passing case.
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ // Ensure that column names and column types can be fetched even if the statement is not
+ // stepped. A new SQL statement is created to avoid interaction from the statement cache.
+ mDatabase.beginTransactionReadOnly();
+ try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1 WHERE j = 3")) {
+ // Do not step the statement.
+ assertEquals("i", s.getColumnName(0));
+ assertEquals("j", s.getColumnName(1));
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ mDatabase.beginTransactionReadOnly();
+ try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+ // Do not step the statement.
+ s.getColumnName(3); // out-of-range column
+ fail("JNI exception not thrown");
+ } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
+ // Passing case.
+ } finally {
+ mDatabase.endTransaction();
+ }
+
}
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 18e4fde280ec..4a227d8ff1ef 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -318,11 +318,11 @@ class DisplayTopologyTest {
verifyDisplay(actualDisplay2, id = 2, width = 200f, height = 600f, POSITION_RIGHT,
offset = 0f, noOfChildren = 2)
- val actualDisplay3 = actualDisplay2.children[1]
+ val actualDisplay3 = actualDisplay2.children[0]
verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT,
offset = 10f, noOfChildren = 0)
- val actualDisplay4 = actualDisplay2.children[0]
+ val actualDisplay4 = actualDisplay2.children[1]
verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT,
offset = 210f, noOfChildren = 0)
}
@@ -402,42 +402,46 @@ class DisplayTopologyTest {
@Test
fun rearrange_twoDisplays() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// Arrange in staggered manner, connected vertically.
RectF(100f, 100f, 250f, 200f),
RectF(150f, 200f, 300f, 300f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).isEmpty()
- assertPositioning(nodes, Pair(POSITION_BOTTOM, 50f))
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 1)
+ val node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 150f, height = 100f, POSITION_BOTTOM, offset = 50f,
+ noOfChildren = 0)
}
@Test
fun rearrange_reverseOrderOfSeveralDisplays() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
RectF(0f, 0f, 150f, 100f),
RectF(-150f, 0f, 0f, 100f),
RectF(-300f, 0f, -150f, 100f),
RectF(-450f, 0f, -300f, 100f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_LEFT, 0f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- assertThat(nodes[3].children).isEmpty()
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 3, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_crossWithRootInCenter() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
RectF(0f, 0f, 150f, 100f),
RectF(-150f, 0f, 0f, 100f),
RectF(0f, -100f, 150f, 0f),
@@ -445,21 +449,24 @@ class DisplayTopologyTest {
RectF(0f, 100f, 150f, 200f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_TOP, 0f),
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_BOTTOM, 0f),
- )
-
- assertThat(nodes[0].children)
- .containsExactly(nodes[1], nodes[2], nodes[3], nodes[4])
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 4)
+ verifyDisplay(
+ root.children[0], id = 1, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[1], id = 2, width = 150f, height = 100f, POSITION_TOP, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[2], id = 3, width = 150f, height = 100f, POSITION_RIGHT, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[3], id = 4, width = 150f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_elbowArrangementDoesNotUseCornerAdjacency1() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 2
// |
// 0 - 1
@@ -469,20 +476,20 @@ class DisplayTopologyTest {
RectF(100f, -100f, 200f, 0f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).isEmpty()
-
- assertPositioning(
- nodes,
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_TOP, 0f),
- )
+ verifyDisplay(root, id = 0, width = 100f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 100f, height = 100f, POSITION_RIGHT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 100f, height = 100f, POSITION_TOP,
+ offset = 0f, noOfChildren = 0)
}
@Test
fun rearrange_elbowArrangementDoesNotUseCornerAdjacency2() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 0
// |
// 1
@@ -495,22 +502,24 @@ class DisplayTopologyTest {
RectF(-100f, 200f, 0f, 300f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- assertThat(nodes[3].children).isEmpty()
-
- assertPositioning(
- nodes,
- Pair(POSITION_BOTTOM, 0f),
- Pair(POSITION_BOTTOM, 0f),
- Pair(POSITION_LEFT, 0f),
- )
+ verifyDisplay(root, id = 0, width = 100f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 100f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 100f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 3, width = 100f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_useLargerEdge() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 444111
// 444111
// 444111
@@ -527,23 +536,24 @@ class DisplayTopologyTest {
RectF(0f, 0f, 30f, 30f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_TOP, 10f),
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_BOTTOM, -10f),
- Pair(POSITION_LEFT, 0f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1], nodes[2])
- assertThat(nodes[1].children).containsExactly(nodes[4])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- (3..4).forEach { assertThat(nodes[it].children).isEmpty() }
+ verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 2)
+ verifyDisplay(
+ root.children[0], id = 1, width = 30f, height = 30f, POSITION_TOP,
+ offset = 10f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[0].children[0], id = 4, width = 30f, height = 30f, POSITION_LEFT,
+ offset = 0f, noOfChildren = 0)
+ verifyDisplay(
+ root.children[1], id = 2, width = 30f, height = 30f, POSITION_RIGHT,
+ offset = 0f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[1].children[0], id = 3, width = 30f, height = 30f, POSITION_BOTTOM,
+ offset = -10f, noOfChildren = 0)
}
@Test
fun rearrange_closeGaps() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 000
// 000 111
// 000 111
@@ -558,16 +568,14 @@ class DisplayTopologyTest {
// TOP/BOTTOM attach
)
- assertPositioning(
- nodes,
- // In the case of corner adjacency, we prefer a left/right attachment.
- Pair(POSITION_RIGHT, 10f),
- Pair(POSITION_BOTTOM, 30f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).isEmpty()
+ verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[0], id = 1, width = 30f, height = 30f, POSITION_RIGHT, offset = 10f,
+ noOfChildren = 1)
+ // In the case of corner adjacency, we prefer a left/right attachment.
+ verifyDisplay(
+ root.children[0].children[0], id = 2, width = 29.5f, height = 30f, POSITION_BOTTOM,
+ offset = 30f, noOfChildren = 0)
}
@Test
@@ -612,8 +620,10 @@ class DisplayTopologyTest {
/**
* Runs the rearrange algorithm and returns the resulting tree as a list of nodes, with the
* root at index 0. The number of nodes is inferred from the number of positions passed.
+ *
+ * Returns the root node.
*/
- private fun rearrangeRects(vararg pos: RectF): List<DisplayTopology.TreeNode> {
+ private fun rearrangeRects(vararg pos: RectF): DisplayTopology.TreeNode {
// Generates a linear sequence of nodes in order in the List from root to leaf,
// left-to-right. IDs are ascending from 0 to count - 1.
@@ -631,7 +641,7 @@ class DisplayTopologyTest {
PointF(pos[it].left, pos[it].top)
})
- return nodes
+ return nodes[0]
}
private fun verifyDisplay(display: DisplayTopology.TreeNode, id: Int, width: Float,
@@ -644,11 +654,4 @@ class DisplayTopologyTest {
assertThat(display.offset).isEqualTo(offset)
assertThat(display.children).hasSize(noOfChildren)
}
-
- private fun assertPositioning(
- nodes: List<DisplayTopology.TreeNode>, vararg positions: Pair<Int, Float>) {
- assertThat(nodes.drop(1).map { Pair(it.position, it.offset) })
- .containsExactly(*positions)
- .inOrder()
- }
}
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 4172bffe100c..9a679d8e8a96 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -31,6 +31,8 @@ import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.os.SkipPresubmit;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -131,6 +133,7 @@ public class BinderThreadPriorityTest {
}
@Test
+ @SkipPresubmit("b/381950874: bitrot and failed")
public void testPassPriorityToService() throws Exception {
for (int prio = 19; prio >= -20; prio--) {
Process.setThreadPriority(prio);
@@ -146,6 +149,7 @@ public class BinderThreadPriorityTest {
}
@Test
+ @SkipPresubmit("b/381950874: bitrot and failed")
public void testCallBackFromServiceWithPriority() throws Exception {
for (int prio = -20; prio <= 19; prio++) {
final int expected = prio;
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2a67716aa215..0bbfb6566d00 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -178,6 +178,20 @@ public class BuildTest {
}
@Test
+ public void testParseFullVersionIncorrectInputMajorVersionTooLarge() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("40000.1");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputMinorVersionTooLarge() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("3.99999999");
+ });
+ }
+
+ @Test
public void testFullVersionToStringCorrectInput() throws Exception {
assertEquals("0.0", Build.fullVersionToString(0));
assertEquals("1.0", Build.fullVersionToString(1 * 100000 + 0));
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index e14608ac503d..fc04e6438ac6 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -16,13 +16,20 @@
package android.os;
+import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.app.PropertyInvalidatedCache;
+import android.app.PropertyInvalidatedCache.Args;
import android.multiuser.Flags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.os.IpcDataCache;
import androidx.test.filters.SmallTest;
@@ -43,6 +50,10 @@ import org.junit.Test;
@SmallTest
public class IpcDataCacheTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Configuration for creating caches
private static final String MODULE = IpcDataCache.MODULE_TEST;
private static final String API = "testApi";
@@ -287,7 +298,12 @@ public class IpcDataCacheTest {
@Override
public String apply(Integer qv) {
mRecomputeCount += 1;
- return "foo" + qv.toString();
+ // Special case for testing caches of nulls. Integers in the range 30-40 return null.
+ if (qv >= 30 && qv < 40) {
+ return null;
+ } else {
+ return "foo" + qv.toString();
+ }
}
int getRecomputeCount() {
@@ -406,31 +422,16 @@ public class IpcDataCacheTest {
}
@Test
- public void testConfig() {
+ public void testConfigDisable() {
+ // Create a set of caches based on a set of chained configs.
IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA");
TestCache ac = new TestCache(a);
- assertEquals(8, a.maxEntries());
- assertEquals(MODULE, a.module());
- assertEquals("apiA", a.api());
- assertEquals("apiA", a.name());
IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB");
TestCache bc = new TestCache(b);
- assertEquals(8, b.maxEntries());
- assertEquals(MODULE, b.module());
- assertEquals("apiB", b.api());
- assertEquals("apiB", b.name());
IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC");
TestCache cc = new TestCache(c);
- assertEquals(8, c.maxEntries());
- assertEquals(MODULE, c.module());
- assertEquals("apiC", c.api());
- assertEquals("nameC", c.name());
IpcDataCache.Config d = a.child("nameD");
TestCache dc = new TestCache(d);
- assertEquals(8, d.maxEntries());
- assertEquals(MODULE, d.module());
- assertEquals("apiA", d.api());
- assertEquals("nameD", d.name());
a.disableForCurrentProcess();
assertEquals(ac.isDisabled(), true);
@@ -449,6 +450,7 @@ public class IpcDataCacheTest {
assertEquals(ec.isDisabled(), true);
}
+
// Verify that invalidating the cache from an app process would fail due to lack of permissions.
@Test
@android.platform.test.annotations.DisabledOnRavenwood(
@@ -507,4 +509,46 @@ public class IpcDataCacheTest {
// Re-enable test mode (so that the cleanup for the test does not throw).
IpcDataCache.setTestMode(true);
}
+
+ @Test
+ public void testCachingNulls() {
+ IpcDataCache.Config c =
+ new IpcDataCache.Config(4, IpcDataCache.MODULE_TEST, "testCachingNulls");
+ TestCache cache;
+ cache = new TestCache(c.cacheNulls(true));
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+
+ cache = new TestCache(c.cacheNulls(false));
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
+
+ // Verify that the default is not to cache nulls.
+ cache = new TestCache(c);
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
+ }
}
diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
deleted file mode 100644
index 4d64a3a94b41..000000000000
--- a/core/tests/coretests/src/android/os/TestLooperManagerTest.java
+++ /dev/null
@@ -1,91 +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 android.os;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-public class TestLooperManagerTest {
- private static final String TAG = "TestLooperManagerTest";
-
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .setProvideMainThread(true)
- .build();
-
- @Test
- public void testMainThread() throws Exception {
- doTest(Looper.getMainLooper());
- }
-
- @Test
- public void testCustomThread() throws Exception {
- final HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- doTest(thread.getLooper());
- }
-
- private void doTest(Looper looper) throws Exception {
- final TestLooperManager tlm =
- InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper);
-
- final Handler handler = new Handler(looper);
- final CountDownLatch latch = new CountDownLatch(1);
-
- assertFalse(tlm.hasMessages(handler, null, 42));
-
- handler.sendEmptyMessage(42);
- handler.post(() -> {
- latch.countDown();
- });
- assertTrue(tlm.hasMessages(handler, null, 42));
- assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
-
- final Message first = tlm.next();
- assertEquals(42, first.what);
- assertNull(first.callback);
- tlm.execute(first);
- assertFalse(tlm.hasMessages(handler, null, 42));
- assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
- tlm.recycle(first);
-
- final Message second = tlm.next();
- assertNotNull(second.callback);
- tlm.execute(second);
- assertFalse(tlm.hasMessages(handler, null, 42));
- assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
- tlm.recycle(second);
-
- tlm.release();
- }
-}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index c3bd0657c511..108f5ba3f8a8 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -211,6 +211,102 @@ public class InsetsSourceTest {
}
@Test
+ public void testCalculateInsets_partialSideIntersection_leftCenter() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 400), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_leftTop() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, -100, 500, 400), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_leftBottom() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 600), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topCenter() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topLeft() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 400, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topRight() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightCenter() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 400), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightTop() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, -100, 500, 400), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightBottom() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 600), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomCenter() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomLeft() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 400, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomRight() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
public void testCalculateVisibleInsets_override() {
mSource.setFrame(new Rect(0, 0, 500, 100));
mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 402b92a3f2a2..26806b143629 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,16 +16,23 @@
package android.view.textclassifier;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
+import android.Manifest;
import android.content.Context;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,4 +68,28 @@ public class TextClassificationManagerTest {
assertThat(mTcm.getTextClassifier(TextClassifier.SYSTEM))
.isInstanceOf(SystemTextClassifier.class);
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ public void testGetClassifier() {
+ Assume.assumeTrue(Flags.textClassifierChoiceApiEnabled());
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_DEVICE_DEFAULT));
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_ANDROID_DEFAULT));
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_SELF_PROVIDED));
+
+ runWithShellPermissionIdentity(() -> {
+ assertThat(
+ mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_DEVICE_DEFAULT)).isInstanceOf(
+ SystemTextClassifier.class);
+ assertThat(mTcm.getClassifier(
+ TextClassifier.CLASSIFIER_TYPE_ANDROID_DEFAULT)).isInstanceOf(
+ SystemTextClassifier.class);
+ assertThat(mTcm.getClassifier(
+ TextClassifier.CLASSIFIER_TYPE_SELF_PROVIDED)).isSameInstanceAs(
+ TextClassifier.NO_OP);
+ }, Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE);
+ }
}
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 21930d17ed68..b9344261cade 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -33,7 +33,9 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.EmptyActivity;
@@ -48,7 +50,9 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -64,6 +68,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -91,6 +96,8 @@ public class WindowContextTest {
public ActivityTestRule<EmptyActivity> mActivityRule =
new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
false /* launchActivity */);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WindowContext mWindowContext = createWindowContext();
@@ -340,17 +347,35 @@ public class WindowContextTest {
}
@Test
- public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() {
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentToDisplayId_wasAttached_reparentToDisplayAreaPropagatedToTokenController() {
+ final WindowTokenClientController mockWindowTokenClientController =
+ mock(WindowTokenClientController.class);
+ when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(),
+ any())).thenReturn(true);
+ WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);
+
+ mWindowContext.reparentToDisplay(DEFAULT_DISPLAY + 1);
+
+ verify(mockWindowTokenClientController).reparentToDisplayArea(any(),
+ /* displayId= */ eq(DEFAULT_DISPLAY + 1)
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentToDisplayId_sameDisplayId_noReparenting() {
final WindowTokenClientController mockWindowTokenClientController =
mock(WindowTokenClientController.class);
+ when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(),
+ any())).thenReturn(true);
WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);
- mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1);
+ mWindowContext.reparentToDisplay(DEFAULT_DISPLAY);
- verify(mockWindowTokenClientController).detachIfNeeded(any());
- verify(mockWindowTokenClientController).attachToDisplayArea(any(),
- anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1),
- any());
+ verify(mockWindowTokenClientController, never()).reparentToDisplayArea(any(),
+ /* displayId= */ eq(DEFAULT_DISPLAY)
+ );
}
private WindowContext createWindowContext() {
diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java
new file mode 100644
index 000000000000..5f6d806bc064
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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.internal.view;
+
+import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_RELAX_SCROLL_VIEW_CRITERIA;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_FIXED;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_OPAQUE;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_RECYCLING;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_SCROLLING;
+import static com.android.internal.view.ScrollCaptureInternal.detectScrollingType;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.testing.AndroidTestingRunner;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests scrolling detection.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScrollCaptureInternalTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ /**
+ * Tests the effect of padding on scroll capture search dispatch.
+ * <p>
+ * Verifies computation of child visible bounds with padding.
+ */
+ @Test
+ public void testDetectScrollingType_scrolling_notScrollable() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(false)
+ .canScrollDown(false)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_FIXED, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling_noChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(0)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_OPAQUE, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling_partiallyScrolled() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(true)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+ scrollable.scrollTo(0, 100);
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ @EnableFlags(FLAG_SCROLL_CAPTURE_RELAX_SCROLL_VIEW_CRITERIA)
+ public void testDetectScrollingType_scrolling_multipleChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(10)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_recycling() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(10)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_RECYCLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_noChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(0)
+ .canScrollUp(true)
+ .canScrollDown(true)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_OPAQUE, detectScrollingType(scrollable));
+ }
+
+
+ /**
+ * A mock which can exhibit some attributes and behaviors used to detect different types
+ * of scrolling content.
+ */
+ private static class MockScrollable extends ViewGroup {
+ private final int mChildCount;
+ private final boolean mCanScrollUp;
+ private final boolean mCanScrollDown;
+ private final boolean mScrollToEnabled;
+
+ MockScrollable(Context context, Rect bounds, int childCount, boolean canScrollUp,
+ boolean canScrollDown, boolean scrollToEnabled) {
+ super(context);
+ setFrame(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ mCanScrollUp = canScrollUp;
+ mCanScrollDown = canScrollDown;
+ mScrollToEnabled = scrollToEnabled;
+ mChildCount = childCount;
+ }
+
+ private static class Builder {
+ private int mChildCount;
+ private boolean mCanScrollUp;
+ private boolean mCanScrollDown;
+ private boolean mScrollToEnabled = true;
+
+ private final Rect mBounds = new Rect();
+
+ public MockScrollable build(Context context) {
+ return new MockScrollable(context,
+ mBounds, mChildCount, mCanScrollUp, mCanScrollDown,
+ mScrollToEnabled);
+ }
+
+ public Builder canScrollUp(boolean canScrollUp) {
+ mCanScrollUp = canScrollUp;
+ return this;
+ }
+
+ public Builder canScrollDown(boolean canScrollDown) {
+ mCanScrollDown = canScrollDown;
+ return this;
+ }
+
+ public Builder scrollToEnabled(boolean enabled) {
+ mScrollToEnabled = enabled;
+ return this;
+ }
+
+ public Builder childCount(int childCount) {
+ mChildCount = childCount;
+ return this;
+ }
+
+ public Builder bounds(int left, int top, int right, int bottom) {
+ mBounds.set(left, top, right, bottom);
+ return this;
+ }
+ }
+
+ @Override
+ public boolean canScrollVertically(int direction) {
+ if (direction > 0) {
+ return mCanScrollDown;
+ } else if (direction < 0) {
+ return mCanScrollUp;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int getChildCount() {
+ return mChildCount;
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ if (mScrollToEnabled) {
+ super.scrollTo(x, y);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // We don't layout this view.
+ }
+ }
+}
diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp
index 931eac515e31..14a3cdf98436 100644
--- a/core/tests/overlaytests/device_self_targeting/Android.bp
+++ b/core/tests/overlaytests/device_self_targeting/Android.bp
@@ -31,6 +31,7 @@ android_test {
"androidx.test.ext.junit",
"mockito-target-minus-junit4",
"truth",
+ "flag-junit",
],
optimize: {
diff --git a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
index 28d6545c8a5b..bcf1446b3467 100644
--- a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
+++ b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
@@ -16,6 +16,7 @@
package com.android.overlaytest;
+import static android.content.res.Flags.FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO;
import static android.content.Context.MODE_PRIVATE;
import static android.content.pm.PackageManager.SIGNATURE_NO_MATCH;
@@ -41,6 +42,8 @@ import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
@@ -76,6 +79,8 @@ import java.util.List;
*/
@RunWith(AndroidJUnit4.class)
public class OverlayManagerImplTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String TAG = "OverlayManagerImplTest";
private static final String TARGET_COLOR_RES = "color/mycolor";
@@ -210,6 +215,22 @@ public class OverlayManagerImplTest {
}
@Test
+ @DisableFlags(FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO)
+ public void registerOverlay_forAndroidPackage_shouldFail() {
+ FabricatedOverlayInternal overlayInternal =
+ createOverlayWithName(
+ mOverlayName,
+ SYSTEM_APP_OVERLAYABLE,
+ "android",
+ List.of(Pair.create("color/white", Pair.create(null, Color.BLACK))));
+
+ assertThrows(
+ "Wrong target package name",
+ IllegalArgumentException.class,
+ () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal));
+ }
+
+ @Test
public void getOverlayInfosForTarget_defaultShouldBeZero() {
List<OverlayInfo> overlayInfos =
mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName());
diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp
index 634098074cca..9b7200436f02 100644
--- a/core/tests/overlaytests/host/Android.bp
+++ b/core/tests/overlaytests/host/Android.bp
@@ -28,14 +28,14 @@ java_test_host {
test_suites: [
"device-tests",
],
- target_required: [
- "OverlayHostTests_NonPlatformSignatureOverlay",
- "OverlayHostTests_PlatformSignatureStaticOverlay",
- "OverlayHostTests_PlatformSignatureOverlay",
- "OverlayHostTests_UpdateOverlay",
- "OverlayHostTests_FrameworkOverlayV1",
- "OverlayHostTests_FrameworkOverlayV2",
- "OverlayHostTests_AppOverlayV1",
- "OverlayHostTests_AppOverlayV2",
+ device_common_data: [
+ ":OverlayHostTests_NonPlatformSignatureOverlay",
+ ":OverlayHostTests_PlatformSignatureStaticOverlay",
+ ":OverlayHostTests_PlatformSignatureOverlay",
+ ":OverlayHostTests_UpdateOverlay",
+ ":OverlayHostTests_FrameworkOverlayV1",
+ ":OverlayHostTests_FrameworkOverlayV2",
+ ":OverlayHostTests_AppOverlayV1",
+ ":OverlayHostTests_AppOverlayV2",
],
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 3b9f35b1eb68..e6586b3c2583 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -496,4 +496,58 @@ public class ArrayUtilsTest {
// expected
}
}
+
+ // Note: the zeroize() tests only test the behavior that can be tested from a Java test.
+ // They do not verify that no copy of the data is left anywhere.
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableByteArray() {
+ final int length = 10;
+ byte[] array = ArrayUtils.newNonMovableByteArray(length);
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularByteArray() {
+ final int length = 10;
+ byte[] array = new byte[length];
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableCharArray() {
+ final int length = 10;
+ char[] array = ArrayUtils.newNonMovableCharArray(length);
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularCharArray() {
+ final int length = 10;
+ char[] array = new char[length];
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroize_acceptsNull() {
+ ArrayUtils.zeroize((byte[]) null);
+ ArrayUtils.zeroize((char[]) null);
+ }
}
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 38ea4ac8d109..45952ea75b6f 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -94,5 +94,6 @@
<permission name="android.permission.CONTROL_UI_TRACING" />
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"/>
+ <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a26f5e383586..2398e7134b34 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -419,6 +419,7 @@ applications that come with the platform
<permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
+ <permission name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
<permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<permission name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" />
@@ -606,6 +607,8 @@ applications that come with the platform
<!-- Permission required for CTS test - IntrusionDetectionManagerTest -->
<permission name="android.permission.READ_INTRUSION_DETECTION_STATE" />
<permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" />
+ <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
+ <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
@@ -676,4 +679,8 @@ applications that come with the platform
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.ENTER_TRADE_IN_MODE"/>
</privapp-permissions>
+
+ <privapp-permissions package="com.android.wm.shell">
+ <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+ </privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 90723b2f1493..906c71d9caca 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -49,11 +49,22 @@ public final class BLASTBufferQueue {
private static native void nativeSetTransactionHangCallback(long ptr,
TransactionHangCallback callback);
private static native void nativeSetApplyToken(long ptr, IBinder applyToken);
+ private static native void nativeSetWaitForBufferReleaseCallback(long ptr,
+ WaitForBufferReleaseCallback callback);
public interface TransactionHangCallback {
void onTransactionHang(String reason);
}
+
+ public interface WaitForBufferReleaseCallback {
+ /**
+ * Indicates that the client is waiting on buffer release
+ * due to no free buffers being available to render into.
+ */
+ void onWaitForBufferRelease();
+ }
+
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@PixelFormat.Format int format) {
@@ -210,4 +221,11 @@ public final class BLASTBufferQueue {
public void setApplyToken(IBinder applyToken) {
nativeSetApplyToken(mNativeObject, applyToken);
}
+
+ /**
+ * Propagate callback about being blocked on buffer release.
+ */
+ public void setWaitForBufferReleaseCallback(WaitForBufferReleaseCallback waitCallback) {
+ nativeSetWaitForBufferReleaseCallback(mNativeObject, waitCallback);
+ }
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9bf4d65e1865..2e885145819c 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -34,6 +34,7 @@ import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
import android.graphics.text.TextRunShaper;
import android.os.Build;
@@ -2141,6 +2142,14 @@ public class Paint {
* @see FontVariationAxis
*/
public boolean setFontVariationSettings(String fontVariationSettings) {
+ return setFontVariationSettings(fontVariationSettings, 0 /* wght adjust */);
+ }
+
+ /**
+ * Set font variation settings with weight adjustment
+ * @hide
+ */
+ public boolean setFontVariationSettings(String fontVariationSettings, int wghtAdjust) {
final boolean useFontVariationStore = Flags.typefaceRedesignReadonly()
&& CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
if (useFontVariationStore) {
@@ -2154,8 +2163,13 @@ public class Paint {
long builderPtr = nCreateFontVariationBuilder(axes.length);
for (int i = 0; i < axes.length; ++i) {
- nAddFontVariationToBuilder(builderPtr, axes[i].getOpenTypeTagValue(),
- axes[i].getStyleValue());
+ int tag = axes[i].getOpenTypeTagValue();
+ float value = axes[i].getStyleValue();
+ if (tag == 0x77676874 /* wght */) {
+ value = Math.clamp(value + wghtAdjust,
+ FontStyle.FONT_WEIGHT_MIN, FontStyle.FONT_WEIGHT_MAX);
+ }
+ nAddFontVariationToBuilder(builderPtr, tag, value);
}
nSetFontVariationOverride(mNativePaint, builderPtr);
mFontVariationSettings = fontVariationSettings;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 9ea2943bc6da..f0613cec6a0b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -398,27 +398,23 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
new TaskFragmentAnimationParams.Builder();
final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes);
builder.setAnimationBackgroundColor(animationBackgroundColor);
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final int openAnimationResId =
- splitAttributes.getAnimationParams().getOpenAnimationResId();
- builder.setOpenAnimationResId(openAnimationResId);
- final int closeAnimationResId =
- splitAttributes.getAnimationParams().getCloseAnimationResId();
- builder.setCloseAnimationResId(closeAnimationResId);
- final int changeAnimationResId =
- splitAttributes.getAnimationParams().getChangeAnimationResId();
- builder.setChangeAnimationResId(changeAnimationResId);
- }
+ final int openAnimationResId =
+ splitAttributes.getAnimationParams().getOpenAnimationResId();
+ builder.setOpenAnimationResId(openAnimationResId);
+ final int closeAnimationResId =
+ splitAttributes.getAnimationParams().getCloseAnimationResId();
+ builder.setCloseAnimationResId(closeAnimationResId);
+ final int changeAnimationResId =
+ splitAttributes.getAnimationParams().getChangeAnimationResId();
+ builder.setChangeAnimationResId(changeAnimationResId);
return builder.build();
}
@ColorInt
private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) {
int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
- AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- animationBackground = splitAttributes.getAnimationParams().getAnimationBackground();
- }
+ final AnimationBackground animationBackground =
+ splitAttributes.getAnimationParams().getAnimationBackground();
if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {
animationBackgroundColor = colorBackground.getColor();
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 714d5e0de367..5f1fb4b44613 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -132,13 +132,6 @@ flag {
}
flag {
- name: "enable_bubble_bar_in_persistent_task_bar"
- namespace: "multitasking"
- description: "Enable bubble bar to be shown in the persistent task bar"
- bug: "346391377"
-}
-
-flag {
name: "bubble_view_info_executors"
namespace: "multitasking"
description: "Use executors to inflate bubble views"
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt
new file mode 100644
index 000000000000..ef8e71c2590b
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+import com.android.wm.shell.common.ShellExecutor
+
+/**
+ * Simple implementation of [ShellExecutor] that collects all runnables and executes them
+ * sequentially once [flushAll] is called
+ */
+class TestShellExecutor : ShellExecutor {
+
+ private val runnables: MutableList<Runnable> = mutableListOf()
+
+ override fun execute(runnable: Runnable) {
+ runnables.add(runnable)
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ execute(runnable)
+ }
+
+ override fun removeCallbacks(runnable: Runnable?) {}
+
+ override fun hasCallback(runnable: Runnable?): Boolean = false
+
+ /**
+ * Execute all posted runnables sequentially
+ */
+ fun flushAll() {
+ while (runnables.isNotEmpty()) {
+ runnables.removeAt(0).run()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index f535fbd653c5..2b4e5417f188 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -34,6 +34,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.Flags
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.properties.ProdBubbleProperties
@@ -41,7 +42,6 @@ import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.draganddrop.DragAndDropController
@@ -84,16 +84,16 @@ class BubbleControllerBubbleBarTest {
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var bubblePositioner: BubblePositioner
private lateinit var bubbleData: BubbleData
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
@@ -232,8 +232,8 @@ class BubbleControllerBubbleBarTest {
bubbleData: BubbleData,
bubbleLogger: BubbleLogger,
bubblePositioner: BubblePositioner,
- mainExecutor: TestExecutor,
- bgExecutor: TestExecutor,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
): BubbleController {
val shellCommandHandler = ShellCommandHandler()
val shellController =
@@ -289,29 +289,6 @@ class BubbleControllerBubbleBarTest {
)
}
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
- }
-
private class FakeBubblesStateListener : Bubbles.BubbleStateListener {
override fun onBubbleStateChange(update: BubbleBarUpdate?) {}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index b38d00da6dfa..1d0c5057c77f 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -602,8 +602,72 @@ class BubblePositionerTest {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
}
+ @Test
+ fun getExpandedViewContainerPadding_largeScreen_fitsMaxViewWidth() {
+ val expandedViewWidth = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_width
+ )
+ // set the screen size so that it is wide enough to fit the maximum width size
+ val screenWidth = expandedViewWidth * 2
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, screenWidth, 2000),
+ isLargeScreen = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ val padding = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding
+ )
+ val right = screenWidth - expandedViewWidth - padding
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, right, 0))
+ }
+
+ @Test
+ fun getExpandedViewContainerPadding_largeScreen_doesNotFitMaxViewWidth() {
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, 600, 2000),
+ isLargeScreen = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ val padding = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding
+ )
+ // the screen is not wide enough to fit the maximum width size, so the view fills the screen
+ // minus left and right padding
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
+ }
+
+ @Test
+ fun getExpandedViewContainerPadding_smallTablet() {
+ val screenWidth = 500
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, screenWidth, 2000),
+ isLargeScreen = true,
+ isSmallTablet = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ // for small tablets, the view width is set to be 0.72 * screen width
+ val viewWidth = (screenWidth * 0.72).toInt()
+ val padding = (screenWidth - viewWidth) / 2
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
+ }
+
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
- positioner.setShowingInBubbleBar(true)
+ positioner.isShowingInBubbleBar = true
val windowBounds = Rect(0, 0, 2000, 2600)
val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 36acf6614272..239acd37f286 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -36,14 +36,11 @@ import com.android.internal.protolog.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
-import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
@@ -71,7 +68,7 @@ class BubbleStackViewTest {
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
- private lateinit var shellExecutor: ShellExecutor
+ private lateinit var shellExecutor: TestShellExecutor
private lateinit var windowManager: WindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
@@ -108,7 +105,7 @@ class BubbleStackViewTest {
)
bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
- bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
+ bubbleTaskViewFactory = FakeBubbleTaskViewFactory(context, shellExecutor)
bubbleStackView =
BubbleStackView(
context,
@@ -168,6 +165,7 @@ class BubbleStackViewTest {
// This will eventually propagate an update back to the stack view, but setting the
// entire pipeline is outside the scope of a unit test.
assertThat(bubbleData.isExpanded).isTrue()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -206,6 +204,7 @@ class BubbleStackViewTest {
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -223,6 +222,7 @@ class BubbleStackViewTest {
// tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble1.iconView!!.performClick()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
@@ -233,6 +233,7 @@ class BubbleStackViewTest {
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
bubble1.iconView!!.performClick()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -426,55 +427,4 @@ class BubbleStackViewTest {
override fun hideCurrentInputMethod() {}
}
-
- private class TestShellExecutor : ShellExecutor {
-
- override fun execute(runnable: Runnable) {
- runnable.run()
- }
-
- override fun executeDelayed(r: Runnable, delayMillis: Long) {
- r.run()
- }
-
- override fun removeCallbacks(r: Runnable?) {}
-
- override fun hasCallback(r: Runnable): Boolean = false
- }
-
- private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
- override fun create(): BubbleTaskView {
- val taskViewTaskController = mock<TaskViewTaskController>()
- val taskView = TaskView(context, taskViewTaskController)
- return BubbleTaskView(taskView, shellExecutor)
- }
- }
-
- private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
-
- override val overflowBubbles: List<Bubble>
- get() = emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {}
-
- override fun collapseStack() {}
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {}
-
- override fun removeBubble(key: String, reason: Int) {}
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {}
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {}
-
- override fun isStackExpanded(): Boolean = false
-
- override fun isShowingAsBubbleBar(): Boolean = false
-
- override fun hideCurrentInputMethod() {}
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 776ea9226b06..680d015dfd2f 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -35,13 +35,13 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.properties.BubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
@@ -70,8 +70,8 @@ class BubbleViewInfoTaskTest {
private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
private lateinit var iconFactory: BubbleIconFactory
private lateinit var bubbleController: BubbleController
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
private lateinit var bubbleStackView: BubbleStackView
private lateinit var bubblePositioner: BubblePositioner
private lateinit var bubbleLogger: BubbleLogger
@@ -94,8 +94,8 @@ class BubbleViewInfoTaskTest {
context.resources.getDimensionPixelSize(R.dimen.importance_ring_stroke_width)
)
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
val shellInit = ShellInit(mainExecutor)
val shellCommandHandler = ShellCommandHandler()
@@ -335,27 +335,4 @@ class BubbleViewInfoTaskTest {
bgExecutor
)
}
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt
new file mode 100644
index 000000000000..3c013d3636e8
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.bubbles
+
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import java.util.Collections
+
+/** Fake implementation of [BubbleExpandedViewManager] for testing. */
+class FakeBubbleExpandedViewManager(var bubbleBar: Boolean = false, var expanded: Boolean = false) :
+ BubbleExpandedViewManager {
+
+ override val overflowBubbles: List<Bubble>
+ get() = Collections.emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {}
+
+ override fun collapseStack() {}
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {}
+
+ override fun removeBubble(key: String, reason: Int) {}
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {}
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {}
+
+ override fun isStackExpanded(): Boolean {
+ return expanded
+ }
+
+ override fun isShowingAsBubbleBar(): Boolean {
+ return bubbleBar
+ }
+
+ override fun hideCurrentInputMethod() {}
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
index 3279d561d4f1..bcaa63bfad36 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
@@ -19,6 +19,10 @@ package com.android.wm.shell.bubbles
import android.content.Context
import android.content.pm.ShortcutInfo
import android.content.res.Resources
+import android.view.LayoutInflater
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.BubbleViewInfoTask.BubbleViewInfo
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
import com.google.common.util.concurrent.MoreExecutors.directExecutor
@@ -27,6 +31,33 @@ import com.google.common.util.concurrent.MoreExecutors.directExecutor
class FakeBubbleFactory {
companion object {
+ fun createExpandedView(
+ context: Context,
+ bubblePositioner: BubblePositioner,
+ expandedViewManager: BubbleExpandedViewManager,
+ bubbleTaskView: BubbleTaskView,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
+ bubbleLogger: BubbleLogger = BubbleLogger(UiEventLoggerFake()),
+ ): BubbleBarExpandedView {
+ val bubbleBarExpandedView =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */)
+ as BubbleBarExpandedView)
+ .apply {
+ initialize(
+ expandedViewManager,
+ bubblePositioner,
+ bubbleLogger,
+ false, /* isOverflow */
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ null, /* regionSamplingProvider */
+ )
+ }
+ return bubbleBarExpandedView
+ }
fun createViewInfo(bubbleExpandedView: BubbleBarExpandedView): BubbleViewInfo {
return BubbleViewInfo().apply { bubbleBarExpandedView = bubbleExpandedView }
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt
new file mode 100644
index 000000000000..42b66aa29bfc
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.bubbles
+
+import android.app.ActivityManager
+import android.content.Context
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Implementation of [BubbleTaskViewFactory] for testing.
+ */
+class FakeBubbleTaskViewFactory(
+ private val context: Context,
+ private val mainExecutor: ShellExecutor,
+) : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ return BubbleTaskView(taskView, mainExecutor)
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
new file mode 100644
index 000000000000..0d8f80935f5a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.bubbles.bar
+
+import android.content.Context
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.core.animation.AnimatorTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubbleLogger
+import com.android.wm.shell.bubbles.BubbleOverflow
+import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
+import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [BubbleBarAnimationHelper] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarAnimationHelperTest {
+
+ companion object {
+ @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+
+ const val SCREEN_WIDTH = 2000
+ const val SCREEN_HEIGHT = 1000
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ private lateinit var animationHelper: BubbleBarAnimationHelper
+ private lateinit var bubblePositioner: BubblePositioner
+ private lateinit var expandedViewManager: BubbleExpandedViewManager
+ private lateinit var bubbleLogger: BubbleLogger
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
+ private lateinit var container: FrameLayout
+
+ @Before
+ fun setUp() {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ ProtoLog.init()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ bubblePositioner = BubblePositioner(context, windowManager)
+ bubblePositioner.setShowingInBubbleBar(true)
+ val deviceConfig =
+ DeviceConfig(
+ windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ isLargeScreen = true,
+ isSmallTablet = false,
+ isLandscape = true,
+ isRtl = false,
+ insets = Insets.of(10, 20, 30, 40),
+ )
+ bubblePositioner.update(deviceConfig)
+ expandedViewManager = FakeBubbleExpandedViewManager()
+ bubbleLogger = BubbleLogger(UiEventLoggerFake())
+
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
+
+ container = FrameLayout(context)
+
+ animationHelper = BubbleBarAnimationHelper(context, bubblePositioner)
+ }
+
+ @After
+ fun tearDown() {
+ bgExecutor.flushAll()
+ mainExecutor.flushAll()
+ }
+
+ @Test
+ fun animateSwitch_bubbleToBubble_oldHiddenNewShown() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(fromBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(fromBubble.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+ assertThat(fromBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+
+ assertThat(toBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(toBubble.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+ }
+
+ @Test
+ fun animateSwitch_bubbleToBubble_handleColorTransferred() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ fromBubble.bubbleBarExpandedView!!
+ .handleView
+ .updateHandleColor(/* isRegionDark= */ true, /* animated= */ false)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble, /* afterAnimation= */ null)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(toBubble.bubbleBarExpandedView!!.handleView.handleColor)
+ .isEqualTo(fromBubble.bubbleBarExpandedView!!.handleView.handleColor)
+ }
+
+ @Test
+ fun animateSwitch_bubbleToOverflow_oldHiddenNewShown() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ val overflow = createOverflow().initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, overflow, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(fromBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(fromBubble.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+ assertThat(fromBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+
+ assertThat(overflow.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(overflow.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun animateSwitch_overflowToBubble_oldHiddenNewShown() {
+ val overflow = createOverflow().initialize(container)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(overflow, toBubble, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(overflow.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(overflow.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+
+ assertThat(toBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(toBubble.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+ }
+
+ private fun createBubble(key: String): Bubble {
+ val bubbleBarExpandedView =
+ FakeBubbleFactory.createExpandedView(
+ context,
+ bubblePositioner,
+ expandedViewManager,
+ FakeBubbleTaskViewFactory(context, mainExecutor).create(),
+ mainExecutor,
+ bgExecutor,
+ bubbleLogger,
+ )
+ val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
+ return FakeBubbleFactory.createChatBubble(context, key, viewInfo)
+ }
+
+ private fun createOverflow(): BubbleOverflow {
+ val overflow = BubbleOverflow(context, bubblePositioner)
+ overflow.initializeForBubbleBar(expandedViewManager, bubblePositioner, bubbleLogger)
+ return overflow
+ }
+
+ private fun Bubble.initialize(container: ViewGroup): Bubble {
+ getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) }
+ // Mark taskView's visible
+ bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ return this
+ }
+
+ private fun BubbleOverflow.initialize(container: ViewGroup): BubbleOverflow {
+ getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) }
+ return this
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 1bf6af8d1f6d..bfc798bb9c79 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -33,32 +33,30 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubble
-import com.android.wm.shell.bubbles.BubbleData
import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.RegionSamplingProvider
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
-import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.google.common.util.concurrent.MoreExecutors.directExecutor
-import java.util.Collections
-import java.util.concurrent.Executor
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import java.util.concurrent.Executor
/** Tests for [BubbleBarExpandedViewTest] */
@SmallTest
@@ -72,8 +70,8 @@ class BubbleBarExpandedViewTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private val windowManager = context.getSystemService(WindowManager::class.java)
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
private lateinit var expandedViewManager: BubbleExpandedViewManager
private lateinit var positioner: BubblePositioner
@@ -90,8 +88,8 @@ class BubbleBarExpandedViewTest {
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
positioner = BubblePositioner(context, windowManager)
positioner.setShowingInBubbleBar(true)
val deviceConfig =
@@ -105,7 +103,7 @@ class BubbleBarExpandedViewTest {
)
positioner.update(deviceConfig)
- expandedViewManager = createExpandedViewManager()
+ expandedViewManager = FakeBubbleExpandedViewManager(bubbleBar = true, expanded = true)
bubbleTaskView = FakeBubbleTaskViewFactory().create()
val inflater = LayoutInflater.from(context)
@@ -426,63 +424,4 @@ class BubbleBarExpandedViewTest {
setWindowInvisible = false
}
}
-
- private fun createExpandedViewManager(): BubbleExpandedViewManager {
- return object : BubbleExpandedViewManager {
- override val overflowBubbles: List<Bubble>
- get() = Collections.emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {
- }
-
- override fun collapseStack() {
- }
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {
- }
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {
- }
-
- override fun removeBubble(key: String, reason: Int) {
- }
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {
- }
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {
- }
-
- override fun isStackExpanded(): Boolean {
- return true
- }
-
- override fun isShowingAsBubbleBar(): Boolean {
- return true
- }
-
- override fun hideCurrentInputMethod() {
- }
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {
- }
- }
- }
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
- }
-} \ No newline at end of file
+}
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 7280f8aa07a6..04c9ffbac287 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
@@ -16,14 +16,12 @@
package com.android.wm.shell.bubbles.bar
-import android.app.ActivityManager
import android.content.Context
import android.content.pm.LauncherApps
import android.graphics.PointF
import android.os.Handler
import android.os.UserManager
import android.view.IWindowManager
-import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
@@ -37,19 +35,19 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleController
import com.android.wm.shell.bubbles.BubbleData
import com.android.wm.shell.bubbles.BubbleDataRepository
import com.android.wm.shell.bubbles.BubbleEducationController
-import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.bubbles.BubbleTaskView
-import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.bubbles.properties.BubbleProperties
@@ -57,7 +55,6 @@ import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
@@ -66,20 +63,16 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.taskview.TaskViewTaskController
import com.android.wm.shell.taskview.TaskViewTransitions
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import org.junit.After
-import java.util.Collections
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
/** Tests for [BubbleBarLayerView] */
@SmallTest
@@ -87,8 +80,7 @@ import org.mockito.kotlin.whenever
class BubbleBarLayerViewTest {
companion object {
- @JvmField @ClassRule
- val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
}
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -112,8 +104,8 @@ class BubbleBarLayerViewTest {
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
- val mainExecutor = TestExecutor()
- val bgExecutor = TestExecutor()
+ val mainExecutor = TestShellExecutor()
+ val bgExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
@@ -145,24 +137,18 @@ class BubbleBarLayerViewTest {
bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData, bubbleLogger)
- val expandedViewManager = createExpandedViewManager()
- val bubbleTaskView = FakeBubbleTaskViewFactory(mainExecutor).create()
+ val expandedViewManager = FakeBubbleExpandedViewManager(bubbleBar = true, expanded = true)
+ val bubbleTaskView = FakeBubbleTaskViewFactory(context, mainExecutor).create()
val bubbleBarExpandedView =
- (LayoutInflater.from(context)
- .inflate(R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */)
- as BubbleBarExpandedView)
- .apply {
- initialize(
- expandedViewManager,
- bubblePositioner,
- bubbleLogger,
- false /* isOverflow */,
- bubbleTaskView,
- mainExecutor,
- bgExecutor,
- null, /* regionSamplingProvider */
- )
- }
+ FakeBubbleFactory.createExpandedView(
+ context,
+ bubblePositioner,
+ expandedViewManager,
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ bubbleLogger,
+ )
val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
bubble = FakeBubbleFactory.createChatBubble(context, viewInfo = viewInfo)
@@ -179,8 +165,8 @@ class BubbleBarLayerViewTest {
windowManager: WindowManager?,
bubbleLogger: BubbleLogger,
bubblePositioner: BubblePositioner,
- mainExecutor: TestExecutor,
- bgExecutor: TestExecutor,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
): BubbleController {
val shellInit = ShellInit(mainExecutor)
val shellCommandHandler = ShellCommandHandler()
@@ -310,74 +296,9 @@ class BubbleBarLayerViewTest {
getInstrumentation().waitForIdleSync()
getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(200) }
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
- AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
- }
-
- private inner class FakeBubbleTaskViewFactory(private val mainExecutor: ShellExecutor) :
- BubbleTaskViewFactory {
- override fun create(): BubbleTaskView {
- val taskViewTaskController = mock<TaskViewTaskController>()
- val taskView = TaskView(context, taskViewTaskController)
- val taskInfo = mock<ActivityManager.RunningTaskInfo>()
- whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
- return BubbleTaskView(taskView, mainExecutor)
- }
- }
-
- private fun createExpandedViewManager(): BubbleExpandedViewManager {
- return object : BubbleExpandedViewManager {
- override val overflowBubbles: List<Bubble>
- get() = Collections.emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {}
-
- override fun collapseStack() {}
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {}
-
- override fun removeBubble(key: String, reason: Int) {}
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {}
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {}
-
- override fun isStackExpanded(): Boolean {
- return true
- }
-
- override fun isShowingAsBubbleBar(): Boolean {
- return true
- }
-
- override fun hideCurrentInputMethod() {}
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
- }
- }
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
+ AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.SCALE_Y,
+ )
}
private fun View.dispatchTouchEvent(eventTime: Long, action: Int, point: PointF) {
diff --git a/libs/WindowManager/Shell/res/color/bubble_drop_target_background_color.xml b/libs/WindowManager/Shell/res/color/bubble_drop_target_background_color.xml
index ab1ab984fd5f..87d36288cfd5 100644
--- a/libs/WindowManager/Shell/res/color/bubble_drop_target_background_color.xml
+++ b/libs/WindowManager/Shell/res/color/bubble_drop_target_background_color.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:alpha="0.35" android:color="?androidprv:attr/materialColorPrimaryContainer" />
+ <item android:alpha="0.35" android:color="@androidprv:color/materialColorPrimaryContainer" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
index 640d184e641c..047f22fe0383 100644
--- a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
@@ -22,5 +22,5 @@
android:color="?androidprv:attr/colorAccentPrimary"/>
<item android:state_selected="true"
android:color="?androidprv:attr/colorAccentPrimary"/>
- <item android:color="?androidprv:attr/materialColorOutlineVariant"/>
+ <item android:color="@androidprv:color/materialColorOutlineVariant"/>
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/open_by_default_settings_dialog_radio_button_color.xml b/libs/WindowManager/Shell/res/color/open_by_default_settings_dialog_radio_button_color.xml
index 0f9b28a07bde..9741b94f3ddb 100644
--- a/libs/WindowManager/Shell/res/color/open_by_default_settings_dialog_radio_button_color.xml
+++ b/libs/WindowManager/Shell/res/color/open_by_default_settings_dialog_radio_button_color.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_checked="true"
- android:color="?androidprv:attr/materialColorPrimaryContainer"/>
- <item android:color="?androidprv:attr/materialColorSurfaceContainer"/>
+ android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <item android:color="@androidprv:color/materialColorSurfaceContainer"/>
</selector>
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_drop_target_background.xml b/libs/WindowManager/Shell/res/drawable/bubble_drop_target_background.xml
index b928a0b20764..20e00e1a9b90 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_drop_target_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_drop_target_background.xml
@@ -21,6 +21,6 @@
<solid android:color="@color/bubble_drop_target_background_color" />
<stroke
android:width="1dp"
- android:color="?androidprv:attr/materialColorPrimaryContainer" />
+ android:color="@androidprv:color/materialColorPrimaryContainer" />
</shape>
</inset>
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
index 657720ee6088..5acca45654f0 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
@@ -19,7 +19,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid
- android:color="?androidprv:attr/materialColorSurfaceContainerHigh"
+ android:color="@androidprv:color/materialColorSurfaceContainerHigh"
/>
<corners android:radius="18sp" />
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
index 8fd2e68f6451..f4d1de89d7a3 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
@@ -17,7 +17,7 @@
<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:attr/materialColorSurfaceBright" />
+ <solid android:color="@androidprv:color/materialColorSurfaceBright" />
<corners
android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="?android:attr/dialogCornerRadius"
diff --git a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_exit_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_or_maximize_exit_button_dark.xml
index 5260450e8a13..b6289e2d6dd7 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_exit_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_or_maximize_exit_button_dark.xml
@@ -21,6 +21,6 @@
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
- android:fillColor="@android:color/white"
- android:pathData="M240,840L240,720L120,720L120,640L320,640L320,840L240,840ZM640,840L640,640L840,640L840,720L720,720L720,840L640,840ZM120,320L120,240L240,240L240,120L320,120L320,320L120,320ZM640,320L640,120L720,120L720,240L840,240L840,320L640,320Z"/>
+ android:fillColor="@android:color/black"
+ android:pathData="M520,560L600,560L600,560ZM320,720Q287,720 263.5,696.5Q240,673 240,640L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L320,720ZM320,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L320,160Q320,160 320,160Q320,160 320,160L320,640Q320,640 320,640Q320,640 320,640ZM160,880Q127,880 103.5,856.5Q80,833 80,800L80,240L160,240L160,800Q160,800 160,800Q160,800 160,800L720,800L720,880L160,880ZM320,160L320,160Q320,160 320,160Q320,160 320,160L320,640Q320,640 320,640Q320,640 320,640L320,640Q320,640 320,640Q320,640 320,640L320,160Q320,160 320,160Q320,160 320,160Z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
index 15837adc2c77..5769a851a8f0 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
@@ -18,5 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" />
- <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
+ <solid android:color="@androidprv:color/materialColorSurfaceBright" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
index 9566f2f140c7..bab2c9582089 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
@@ -17,6 +17,6 @@
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerLow" />
<corners android:radius="@dimen/desktop_mode_maximize_menu_corner_radius" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
index a30cfb74bf4a..b03b13427630 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
@@ -20,6 +20,6 @@
android:shape="rectangle">
<corners
android:radius="@dimen/desktop_mode_maximize_menu_buttons_outline_radius"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow"/>
- <stroke android:width="1dp" android:color="?androidprv:attr/materialColorOutlineVariant"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerLow"/>
+ <stroke android:width="1dp" android:color="@androidprv:color/materialColorOutlineVariant"/>
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml
index 645d24df7c26..b24d432cece0 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml
@@ -17,6 +17,6 @@
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerLow" />
<corners android:radius="@dimen/desktop_windowing_education_promo_corner_radius" />
</shape>
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 4e673e65e053..dd1a1b1dca13 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@+id/indicator_solid">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer" />
<corners android:radius="28dp" />
</shape>
</item>
@@ -26,7 +26,7 @@
<shape android:shape="rectangle">
<corners android:radius="28dp" />
<stroke android:width="1dp"
- android:color="?androidprv:attr/materialColorPrimaryContainer"/>
+ android:color="@androidprv:color/materialColorPrimaryContainer"/>
</shape>
</item>
</layer-list>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml
index f37fb8dbe118..527cc31f2f76 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml
@@ -17,6 +17,6 @@
<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:attr/materialColorSurfaceContainerHigh"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh"/>
<corners android:radius="@dimen/letterbox_education_dialog_corner_radius"/>
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
index 3fdd059ca982..5336b3a22897 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
@@ -32,7 +32,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorPrimary"/>
+ <solid android:color="@androidprv:color/materialColorPrimary"/>
<corners android:radius="@dimen/letterbox_education_dialog_button_radius"/>
<padding android:left="@dimen/letterbox_education_dialog_horizontal_padding"
android:top="@dimen/letterbox_education_dialog_vertical_padding"
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml
index 67929dfc5f71..f45d7044423f 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml
@@ -25,6 +25,6 @@
android:pathData="M0,0h32v32h-32z"/>
<path
android:pathData="M5.867,22.667C4.489,21.844 3.389,20.733 2.567,19.333C1.744,17.933 1.333,16.378 1.333,14.667C1.333,12.067 2.233,9.867 4.033,8.067C5.856,6.244 8.067,5.333 10.667,5.333C13.267,5.333 15.467,6.244 17.267,8.067C19.089,9.867 20,12.067 20,14.667C20,16.378 19.589,17.933 18.767,19.333C17.944,20.733 16.844,21.844 15.467,22.667H5.867ZM6.667,20H14.667C15.511,19.356 16.167,18.578 16.633,17.667C17.1,16.733 17.333,15.733 17.333,14.667C17.333,12.822 16.678,11.256 15.367,9.967C14.078,8.656 12.511,8 10.667,8C8.822,8 7.244,8.656 5.933,9.967C4.644,11.256 4,12.822 4,14.667C4,15.733 4.233,16.733 4.7,17.667C5.167,18.578 5.822,19.356 6.667,20ZM7.2,26.667C6.822,26.667 6.5,26.544 6.233,26.3C5.989,26.033 5.867,25.711 5.867,25.333C5.867,24.956 5.989,24.644 6.233,24.4C6.5,24.133 6.822,24 7.2,24H14.133C14.511,24 14.822,24.133 15.067,24.4C15.333,24.644 15.467,24.956 15.467,25.333C15.467,25.711 15.333,26.033 15.067,26.3C14.822,26.544 14.511,26.667 14.133,26.667H7.2ZM10.667,30.667C9.933,30.667 9.3,30.411 8.767,29.9C8.256,29.367 8,28.733 8,28H13.333C13.333,28.733 13.067,29.367 12.533,29.9C12.022,30.411 11.4,30.667 10.667,30.667ZM24.667,13.367C24.667,11.7 24.078,10.278 22.9,9.1C21.722,7.922 20.3,7.333 18.633,7.333C20.3,7.333 21.722,6.756 22.9,5.6C24.078,4.422 24.667,3 24.667,1.333C24.667,3 25.244,4.422 26.4,5.6C27.578,6.756 29,7.333 30.667,7.333C29,7.333 27.578,7.922 26.4,9.1C25.244,10.278 24.667,11.7 24.667,13.367Z"
- android:fillColor="?androidprv:attr/materialColorPrimary"/>
+ android:fillColor="@androidprv:color/materialColorPrimary"/>
</group>
</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
index 4207482260ba..4e77720bd18d 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
@@ -32,7 +32,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorPrimary"/>
+ <solid android:color="@androidprv:color/materialColorPrimary"/>
<corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
<padding android:left="@dimen/letterbox_restart_dialog_horizontal_padding"
android:top="@dimen/letterbox_restart_dialog_vertical_padding"
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
index 72cfeefceffb..90b314a58b8f 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
@@ -17,6 +17,6 @@
<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:attr/materialColorSurfaceContainerHigh"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh"/>
<corners android:radius="@dimen/letterbox_restart_dialog_corner_radius"/>
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
index 816b35063b00..d64e63261ac9 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
@@ -32,9 +32,9 @@
</item>
<item>
<shape android:shape="rectangle">
- <stroke android:color="?androidprv:attr/materialColorOutlineVariant"
+ <stroke android:color="@androidprv:color/materialColorOutlineVariant"
android:width="1dp"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh"/>
<corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
<padding android:left="@dimen/letterbox_restart_dialog_horizontal_padding"
android:top="@dimen/letterbox_restart_dialog_vertical_padding"
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
index f13d26c7f89e..53b4a4b70ed3 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
@@ -25,6 +25,6 @@
android:pathData="M0,0h32v32h-32z"/>
<path
android:pathData="M8.533,25.333H10.667C11.044,25.333 11.356,25.467 11.6,25.733C11.867,25.978 12,26.289 12,26.667C12,27.044 11.867,27.367 11.6,27.633C11.356,27.878 11.044,28 10.667,28H5.333C4.956,28 4.633,27.878 4.367,27.633C4.122,27.367 4,27.044 4,26.667V21.333C4,20.956 4.122,20.644 4.367,20.4C4.633,20.133 4.956,20 5.333,20C5.711,20 6.022,20.133 6.267,20.4C6.533,20.644 6.667,20.956 6.667,21.333V23.467L9.867,20.267C10.111,20.022 10.422,19.9 10.8,19.9C11.178,19.9 11.489,20.022 11.733,20.267C11.978,20.511 12.1,20.822 12.1,21.2C12.1,21.578 11.978,21.889 11.733,22.133L8.533,25.333ZM23.467,25.333L20.267,22.133C20.022,21.889 19.9,21.578 19.9,21.2C19.9,20.822 20.022,20.511 20.267,20.267C20.511,20.022 20.822,19.9 21.2,19.9C21.578,19.9 21.889,20.022 22.133,20.267L25.333,23.467V21.333C25.333,20.956 25.456,20.644 25.7,20.4C25.967,20.133 26.289,20 26.667,20C27.044,20 27.356,20.133 27.6,20.4C27.867,20.644 28,20.956 28,21.333V26.667C28,27.044 27.867,27.367 27.6,27.633C27.356,27.878 27.044,28 26.667,28H21.333C20.956,28 20.633,27.878 20.367,27.633C20.122,27.367 20,27.044 20,26.667C20,26.289 20.122,25.978 20.367,25.733C20.633,25.467 20.956,25.333 21.333,25.333H23.467ZM6.667,8.533V10.667C6.667,11.044 6.533,11.367 6.267,11.633C6.022,11.878 5.711,12 5.333,12C4.956,12 4.633,11.878 4.367,11.633C4.122,11.367 4,11.044 4,10.667V5.333C4,4.956 4.122,4.644 4.367,4.4C4.633,4.133 4.956,4 5.333,4H10.667C11.044,4 11.356,4.133 11.6,4.4C11.867,4.644 12,4.956 12,5.333C12,5.711 11.867,6.033 11.6,6.3C11.356,6.544 11.044,6.667 10.667,6.667H8.533L11.733,9.867C11.978,10.111 12.1,10.422 12.1,10.8C12.1,11.178 11.978,11.489 11.733,11.733C11.489,11.978 11.178,12.1 10.8,12.1C10.422,12.1 10.111,11.978 9.867,11.733L6.667,8.533ZM25.333,8.533L22.133,11.733C21.889,11.978 21.578,12.1 21.2,12.1C20.822,12.1 20.511,11.978 20.267,11.733C20.022,11.489 19.9,11.178 19.9,10.8C19.9,10.422 20.022,10.111 20.267,9.867L23.467,6.667H21.333C20.956,6.667 20.633,6.544 20.367,6.3C20.122,6.033 20,5.711 20,5.333C20,4.956 20.122,4.644 20.367,4.4C20.633,4.133 20.956,4 21.333,4H26.667C27.044,4 27.356,4.133 27.6,4.4C27.867,4.644 28,4.956 28,5.333V10.667C28,11.044 27.867,11.367 27.6,11.633C27.356,11.878 27.044,12 26.667,12C26.289,12 25.967,11.878 25.7,11.633C25.456,11.367 25.333,11.044 25.333,10.667V8.533Z"
- android:fillColor="?androidprv:attr/materialColorPrimary"/>
+ android:fillColor="@androidprv:color/materialColorPrimary"/>
</group>
</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_background.xml
index 4eb22712f5e1..d1a510a03674 100644
--- a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_background.xml
@@ -17,6 +17,6 @@
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer"/>
<corners android:radius="28dp"/>
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml
index 2b2e9df07dce..20e2e7e5a832 100644
--- a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml
@@ -17,6 +17,6 @@
<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:attr/materialColorPrimary"/>
+ <solid android:color="@androidprv:color/materialColorPrimary"/>
<corners android:radius="50dp"/>
</shape>
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 4c7d1c7339fb..7347fbad5f5d 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -29,7 +29,7 @@
<ImageView
android:layout_width="@dimen/bubble_popup_icon_size"
android:layout_height="@dimen/bubble_popup_icon_size"
- android:tint="?androidprv:attr/materialColorOutline"
+ android:tint="@androidprv:color/materialColorOutline"
android:contentDescription="@null"
android:src="@drawable/pip_ic_settings"/>
@@ -41,7 +41,7 @@
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:text="@string/bubble_bar_education_manage_title"/>
<TextView
@@ -51,7 +51,7 @@
android:paddingBottom="@dimen/bubble_popup_padding_bottom"
android:maxWidth="@dimen/bubble_popup_content_max_width"
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textAlignment="center"
android:text="@string/bubble_bar_education_manage_text"/>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml
index e3217811ca29..5750ed7bf8d6 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml
@@ -36,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
</com.android.wm.shell.bubbles.bar.BubbleBarMenuItemView> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
index 7aca921dccc7..cef4f5f960c2 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
@@ -50,8 +50,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
- android:textColor="?androidprv:attr/materialColorOnSurface"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+ android:textColor="@androidprv:color/materialColorOnSurface"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
+ android:textDirection="locale" />
<ImageView
android:id="@+id/bubble_bar_manage_menu_dismiss_icon"
@@ -60,7 +61,7 @@
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@drawable/ic_expand_less"
- app:tint="?androidprv:attr/materialColorOnSurface" />
+ app:tint="@androidprv:color/materialColorOnSurface" />
</LinearLayout>
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 345c399652f9..f0e1871168dd 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -29,7 +29,7 @@
<ImageView
android:layout_width="@dimen/bubble_popup_icon_size"
android:layout_height="@dimen/bubble_popup_icon_size"
- android:tint="?androidprv:attr/materialColorOutline"
+ android:tint="@androidprv:color/materialColorOutline"
android:contentDescription="@null"
android:src="@drawable/ic_floating_landscape"/>
@@ -41,7 +41,7 @@
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:text="@string/bubble_bar_education_stack_title"/>
<TextView
@@ -51,7 +51,7 @@
android:paddingBottom="@dimen/bubble_popup_padding_bottom"
android:maxWidth="@dimen/bubble_popup_content_max_width"
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textAlignment="center"
android:text="@string/bubble_bar_education_stack_text"/>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_flyout.xml b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
index 65a07a718677..deabd564d80a 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
@@ -49,7 +49,7 @@
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:maxLines="1"
android:ellipsize="end"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
<TextView
@@ -59,7 +59,7 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:maxLines="2"
android:ellipsize="end"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
index f88d63d796ea..0c446df69563 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
@@ -28,6 +28,6 @@
android:focusable="true"
android:text="@string/manage_bubbles_text"
android:textSize="@*android:dimen/text_size_body_2_material"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:background="@drawable/bubble_manage_btn_bg"
/> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index d8ae9c8c64a6..4daaf9c6b57f 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -43,7 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
android:text="@string/bubble_dismiss_text" />
@@ -70,7 +70,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
android:text="@string/bubbles_dont_bubble_conversation" />
@@ -98,7 +98,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml b/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml
new file mode 100644
index 000000000000..5a39c83b1e92
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="44dp"
+ android:layout_height="40dp"
+ android:importantForAccessibility="noHideDescendants">
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:progressDrawable="@drawable/circular_progress"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:indeterminate="false"
+ android:layout_marginHorizontal="6dp"
+ android:layout_marginVertical="4dp"
+ android:visibility="invisible"/>
+</FrameLayout>
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 bfd9c818a96e..b69563b46e06 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
@@ -52,7 +52,7 @@
android:layout_height="wrap_content"
tools:text="Gmail"
android:importantForAccessibility="no"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="14sp"
android:textFontWeight="500"
android:lineHeight="20dp"
@@ -69,7 +69,7 @@
android:contentDescription="@string/collapse_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:rotation="180"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:background="?android:selectableItemBackgroundBorderless"/>
</LinearLayout>
@@ -89,7 +89,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/fullscreen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -99,7 +99,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/split_screen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -109,7 +109,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/float_button_text"
android:src="@drawable/desktop_mode_ic_handle_menu_floating"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -118,7 +118,7 @@
android:layout_marginStart="4dp"
android:contentDescription="@string/desktop_text"
android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -139,7 +139,7 @@
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
<Button
@@ -147,7 +147,7 @@
android:contentDescription="@string/new_window_text"
android:text="@string/new_window_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_new_window"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton" />
<Button
@@ -155,7 +155,7 @@
android:contentDescription="@string/manage_windows_text"
android:text="@string/manage_windows_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_manage_windows"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton" />
<Button
@@ -163,7 +163,7 @@
android:contentDescription="@string/change_aspect_ratio_text"
android:text="@string/change_aspect_ratio_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton" />
</LinearLayout>
@@ -183,7 +183,7 @@
android:contentDescription="@string/open_in_browser_text"
android:text="@string/open_in_browser_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
<ImageButton
@@ -195,7 +195,7 @@
android:layout_marginStart="10dp"
android:contentDescription="@string/open_by_default_settings_text"
android:src="@drawable/desktop_mode_ic_handle_menu_open_by_default_settings"
- android:tint="?androidprv:attr/materialColorOnSurface"/>
+ android:tint="@androidprv:color/materialColorOnSurface"/>
</LinearLayout>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 375968ab0ad2..8d7e5fd95957 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -58,7 +58,7 @@
android:fontFamily="google-sans-text"
android:importantForAccessibility="no"
android:text="@string/desktop_mode_maximize_menu_immersive_button_text"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:alpha="0"/>
</LinearLayout>
@@ -89,7 +89,7 @@
android:fontFamily="google-sans-text"
android:importantForAccessibility="no"
android:text="@string/desktop_mode_maximize_menu_maximize_text"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:alpha="0"/>
</LinearLayout>
@@ -140,7 +140,7 @@
android:importantForAccessibility="no"
android:fontFamily="google-sans-text"
android:text="@string/desktop_mode_maximize_menu_snap_text"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:alpha="0"/>
</LinearLayout>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
index bda087b143d0..0a44cd42e790 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
@@ -37,7 +37,7 @@
android:layout_height="wrap_content"
android:lineSpacingExtra="4sp"
android:textAlignment="center"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="14sp"/>
</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
index 488123ad7b0c..cd36983aff2d 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
@@ -64,7 +64,7 @@
android:lineSpacingExtra="4sp"
android:text="@string/letterbox_education_dialog_title"
android:textAlignment="center"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textSize="24sp"/>
@@ -104,7 +104,7 @@
android:background=
"@drawable/letterbox_education_dismiss_button_background_ripple"
android:text="@string/letterbox_education_got_it"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
android:textAlignment="center"
android:contentDescription="@string/letterbox_education_got_it"/>
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index b734d2d81455..059e9e1a7895 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -17,21 +17,14 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <FrameLayout
+ <ViewStub
+ android:id="@+id/stub_progress_bar_container"
+ android:inflatedId="@+id/inflatedProgressBarContainer"
+ android:layout="@layout/desktop_header_maximize_menu_button_progress_indicator_layout"
android:layout_width="44dp"
android:layout_height="40dp"
- android:importantForAccessibility="noHideDescendants">
- <ProgressBar
- android:id="@+id/progress_bar"
- style="?android:attr/progressBarStyleHorizontal"
- android:progressDrawable="@drawable/circular_progress"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:indeterminate="false"
- android:layout_marginHorizontal="6dp"
- android:layout_marginVertical="4dp"
- android:visibility="invisible"/>
- </FrameLayout>
+ android:importantForAccessibility="noHideDescendants"
+ />
<ImageButton
android:id="@+id/maximize_window"
diff --git a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
index b5bceda9a623..f6256e6bc5e7 100644
--- a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
+++ b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
@@ -64,7 +64,7 @@
android:lineHeight="32dp"
android:textFontWeight="400"
android:textSize="24sp"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
tools:text="Gmail" />
<TextView
@@ -75,7 +75,7 @@
android:lineHeight="16dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:text="@string/open_by_default_dialog_subheader_text"/>
<RadioGroup
@@ -121,7 +121,7 @@
android:layout_marginBottom="24dp"
android:textSize="14sp"
android:textFontWeight="500"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
android:background="@drawable/open_by_default_settings_dialog_confirm_button_background"/>
</LinearLayout>
</ScrollView>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index bb53adf69b29..244149b855f6 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -87,7 +87,7 @@
<string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljajte oblačićima u svakom trenutku"</string>
<string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da upravljate time koje aplikacije i razgovori mogu imati oblačić"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
<string name="bubble_shortcut_label" msgid="666269077944378311">"Oblačići"</string>
<string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Prikaz oblačića"</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b7aa1581a5c1..d754243a2b07 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -43,7 +43,7 @@
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
<!-- Letterbox Education -->
- <color name="letterbox_education_text_secondary">?androidprv:attr/materialColorSecondary</color>
+ <color name="letterbox_education_text_secondary">@androidprv:color/materialColorSecondary</color>
<!-- Letterbox Dialog -->
<color name="letterbox_dialog_background">@android:color/system_neutral1_900</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 21ec84d9bc0a..9e2d23b41556 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -266,6 +266,8 @@
<dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen>
<!-- Width of the expanded bubble bar view shown when the bubble is expanded. -->
<dimen name="bubble_bar_expanded_view_width">412dp</dimen>
+ <!-- Offset of the expanded view when it starts sliding in as part of the switch animation -->
+ <dimen name="bubble_bar_expanded_view_switch_offset">48dp</dimen>
<!-- Minimum width of the bubble bar manage menu. -->
<dimen name="bubble_bar_manage_menu_min_width">200dp</dimen>
<!-- Size of the dismiss icon in the bubble bar manage menu. -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 597a921302b7..8a4a7023b8e8 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -48,7 +48,7 @@
<item name="android:paddingEnd">0dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textFontWeight">500</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:background">?android:selectableItemBackground</item>
</style>
@@ -92,7 +92,7 @@
<style name="RestartDialogTitleText">
<item name="android:textSize">24sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:lineSpacingExtra">8sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
@@ -104,23 +104,23 @@
<style name="RestartDialogBodyText" parent="RestartDialogBodyStyle">
<item name="android:letterSpacing">0.02</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:lineSpacingExtra">6sp</item>
</style>
<style name="RestartDialogCheckboxText" parent="RestartDialogBodyStyle">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:lineSpacingExtra">6sp</item>
</style>
<style name="RestartDialogDismissButton" parent="RestartDialogBodyStyle">
<item name="android:lineSpacingExtra">2sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorPrimary</item>
+ <item name="android:textColor">@androidprv:color/materialColorPrimary</item>
</style>
<style name="RestartDialogConfirmButton" parent="RestartDialogBodyStyle">
<item name="android:lineSpacingExtra">2sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimary</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimary</item>
</style>
<style name="ReachabilityEduHandLayout" parent="Theme.AppCompat.Light">
diff --git a/libs/WindowManager/Shell/shared/Android.bp b/libs/WindowManager/Shell/shared/Android.bp
index 5113d980fb7d..c3ee0f76f550 100644
--- a/libs/WindowManager/Shell/shared/Android.bp
+++ b/libs/WindowManager/Shell/shared/Android.bp
@@ -71,6 +71,7 @@ java_library {
srcs: [
"**/desktopmode/*.java",
"**/desktopmode/*.kt",
+ ":wm_shell-shared-utils",
],
static_libs: [
"com.android.window.flags.window-aconfig-java",
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 04c17e54d11f..755f472ee22e 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
@@ -16,9 +16,15 @@
package com.android.wm.shell.shared.desktopmode;
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.SystemProperties;
+import android.view.Display;
+import android.view.WindowManager;
import android.window.DesktopModeFlags;
import com.android.internal.R;
@@ -26,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Constants for desktop mode feature
@@ -35,6 +42,9 @@ public class DesktopModeStatus {
private static final String TAG = "DesktopModeStatus";
+ @Nullable
+ private static Boolean sIsLargeScreenDevice = null;
+
/**
* Flag to indicate whether task resizing is veiled.
*/
@@ -91,6 +101,9 @@ public class DesktopModeStatus {
/** The maximum override density allowed for tasks inside the desktop. */
private static final int DESKTOP_DENSITY_MAX = 1000;
+ /** The number of [WindowDecorViewHost] instances to warm up on system start. */
+ private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2;
+
/**
* Sysprop declaring whether to enters desktop mode by default when the windowing mode of the
* display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM.
@@ -122,6 +135,14 @@ public class DesktopModeStatus {
private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit";
/**
+ * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start.
+ *
+ * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used.
+ */
+ private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP =
+ "persist.wm.debug.desktop_window_decor_pre_warm_size";
+
+ /**
* Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
*/
public static boolean isVeiledResizeEnabled() {
@@ -162,6 +183,27 @@ public class DesktopModeStatus {
}
/**
+ * Return the maximum size of the window decoration surface control view host pool, or zero if
+ * there should be no pooling.
+ */
+ public static int getWindowDecorScvhPoolSize(@NonNull Context context) {
+ if (!Flags.enableDesktopWindowingScvhCacheBugFix()) return 0;
+ final int maxTaskLimit = getMaxTaskLimit(context);
+ if (maxTaskLimit > 0) {
+ return maxTaskLimit;
+ }
+ // TODO: b/368032552 - task limit equal to 0 means unlimited. Figure out what the pool
+ // size should be in that case.
+ return 0;
+ }
+
+ /** The number of [WindowDecorViewHost] instances to warm up on system start. */
+ public static int getWindowDecorPreWarmSize() {
+ return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP,
+ WINDOW_DECOR_PRE_WARM_SIZE);
+ }
+
+ /**
* Return {@code true} if the current device supports desktop mode.
*/
@VisibleForTesting
@@ -195,13 +237,12 @@ public class DesktopModeStatus {
* necessarily enabling desktop mode
*/
public static boolean overridesShowAppHandle(@NonNull Context context) {
- return Flags.showAppHandleLargeScreens()
- && context.getResources().getBoolean(R.bool.config_enableAppHandle);
+ return Flags.showAppHandleLargeScreens() && deviceHasLargeScreen(context);
}
/**
* @return {@code true} if the app handle should be shown because desktop mode is enabled or
- * the device is overriding {@code R.bool.config_enableAppHandle}
+ * the device has a large screen
*/
public static boolean canEnterDesktopModeOrShowAppHandle(@NonNull Context context) {
return canEnterDesktopMode(context) || overridesShowAppHandle(context);
@@ -244,6 +285,21 @@ public class DesktopModeStatus {
}
/**
+ * @return {@code true} if this device has an internal large screen
+ */
+ private static boolean deviceHasLargeScreen(@NonNull Context context) {
+ if (sIsLargeScreenDevice == null) {
+ sIsLargeScreenDevice = Arrays.stream(
+ context.getSystemService(DisplayManager.class)
+ .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+ .anyMatch(display -> display.getMinSizeDimensionDp()
+ >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP);
+ }
+ return sIsLargeScreenDevice;
+ }
+
+ /**
* Return {@code true} if a display should enter desktop mode by default when the windowing mode
* of the display's root [TaskDisplayArea] is set to WINDOWING_MODE_FREEFORM.
*/
@@ -283,6 +339,6 @@ public class DesktopModeStatus {
pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
pw.print(innerPrefix); pw.print("showAppHandle config override=");
- pw.print(context.getResources().getBoolean(R.bool.config_enableAppHandle));
+ pw.print(overridesShowAppHandle(context));
}
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index f9f43bc8dfae..b48296f5f76a 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -58,6 +58,11 @@ public class SplitScreenConstants {
*/
public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
+ /**
+ * Deprecated and will be replaced fully by @SplitIndex. With support for 3+ apps in split,
+ * existing references to top/left and bottom/right will be replaced by INDEX_0 and INDEX_1
+ * respectively. For now they can be used interchangeably, the underlying ints are the same.
+ */
@IntDef(prefix = {"SPLIT_POSITION_"}, value = {
SPLIT_POSITION_UNDEFINED,
SPLIT_POSITION_TOP_OR_LEFT,
@@ -85,6 +90,21 @@ public class SplitScreenConstants {
public @interface SplitIndex {
}
+ /**
+ * Return the @SplitIndex constant for a given integer index. @SplitIndex is the replacement
+ * for @SplitPosition, and will be used interchangeably with @SplitPosition to support 3+ apps
+ * in split.
+ */
+ public static int getIndex(int i) {
+ return switch (i) {
+ case 0 -> SPLIT_INDEX_0;
+ case 1 -> SPLIT_INDEX_1;
+ case 2 -> SPLIT_INDEX_2;
+ case 3 -> SPLIT_INDEX_3;
+ default -> SPLIT_INDEX_UNDEFINED;
+ };
+ }
+
/** Signifies that user is currently not in split screen. */
public static final int NOT_IN_SPLIT = -1;
@@ -159,7 +179,8 @@ public class SplitScreenConstants {
* {@link PersistentSnapPosition} + {@link #NOT_IN_SPLIT}.
*/
@IntDef(value = {
- NOT_IN_SPLIT,
+ NOT_IN_SPLIT, // user is not in split screen
+ SNAP_TO_NONE, // in "free snap mode," where apps are fully resizable
SNAP_TO_2_33_66,
SNAP_TO_2_50_50,
SNAP_TO_2_66_33,
@@ -171,6 +192,23 @@ public class SplitScreenConstants {
})
public @interface SplitScreenState {}
+ /** Converts a {@link SplitScreenState} to a human-readable string. */
+ public static String stateToString(@SplitScreenState int state) {
+ return switch (state) {
+ case NOT_IN_SPLIT -> "NOT_IN_SPLIT";
+ case SNAP_TO_NONE -> "SNAP_TO_NONE";
+ case SNAP_TO_2_33_66 -> "SNAP_TO_2_33_66";
+ case SNAP_TO_2_50_50 -> "SNAP_TO_2_50_50";
+ case SNAP_TO_2_66_33 -> "SNAP_TO_2_66_33";
+ case SNAP_TO_2_90_10 -> "SNAP_TO_2_90_10";
+ case SNAP_TO_2_10_90 -> "SNAP_TO_2_10_90";
+ case SNAP_TO_3_33_33_33 -> "SNAP_TO_3_33_33_33";
+ case SNAP_TO_3_45_45_10 -> "SNAP_TO_3_45_45_10";
+ case SNAP_TO_3_10_45_45 -> "SNAP_TO_3_10_45_45";
+ default -> "UNKNOWN";
+ };
+ }
+
/**
* Checks if the snapPosition in question is a {@link PersistentSnapPosition}.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index d2cef4baf798..f269b3831aab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -268,10 +268,7 @@ class ActivityEmbeddingAnimationRunner {
final Animation animation =
animationProvider.get(info, change, openingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- return new ArrayList<>();
- }
- continue;
+ return new ArrayList<>();
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
info, change, animation, openingWholeScreenBounds);
@@ -296,10 +293,7 @@ class ActivityEmbeddingAnimationRunner {
final Animation animation =
animationProvider.get(info, change, closingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- return new ArrayList<>();
- }
- continue;
+ return new ArrayList<>();
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
info, change, animation, closingWholeScreenBounds);
@@ -455,11 +449,9 @@ class ActivityEmbeddingAnimationRunner {
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds);
// Jump cut if either animation has zero for duration.
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- for (Animation animation : animations) {
- if (shouldUseJumpCutForAnimation(animation)) {
- return new ArrayList<>();
- }
+ for (Animation animation : animations) {
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
}
}
// Keep track as we might need to add background color for the animation.
@@ -516,10 +508,8 @@ class ActivityEmbeddingAnimationRunner {
mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds);
shouldShowBackgroundColor = false;
}
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- if (shouldUseJumpCutForAnimation(animation)) {
- return new ArrayList<>();
- }
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
}
adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change,
TransitionUtil.getRootFor(change, info)));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 3046307702c2..77799e99607b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -96,11 +96,9 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return customAnimation;
- }
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return customAnimation;
}
// Use end bounds for opening.
final Rect bounds = change.getEndAbsBounds();
@@ -130,11 +128,9 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return customAnimation;
- }
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return customAnimation;
}
// Use start bounds for closing.
final Rect bounds = change.getStartAbsBounds();
@@ -168,14 +164,12 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- // TODO(b/293658614): Support more complicated animations that may need more than a noop
- // animation as the start leash.
- final Animation noopAnimation = createNoopAnimation(change);
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return new Animation[]{noopAnimation, customAnimation};
- }
+ // TODO(b/293658614): Support more complicated animations that may need more than a noop
+ // animation as the start leash.
+ final Animation noopAnimation = createNoopAnimation(change);
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return new Animation[]{noopAnimation, customAnimation};
}
// Both start bounds and end bounds are in screen coordinates. We will post translate
// to the local coordinates in ActivityEmbeddingAnimationAdapter#onAnimationUpdate
@@ -320,13 +314,9 @@ class ActivityEmbeddingAnimationSpec {
}
final Animation anim;
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- // TODO(b/293658614): Consider allowing custom animations from non-default packages.
- // Enforce limiting to animations from the default "android" package for now.
- anim = mTransitionAnimation.loadDefaultAnimationRes(resId);
- } else {
- anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), resId);
- }
+ // TODO(b/293658614): Consider allowing custom animations from non-default packages.
+ // Enforce limiting to animations from the default "android" package for now.
+ anim = mTransitionAnimation.loadDefaultAnimationRes(resId);
if (anim != null) {
return anim;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 7243ea36b137..06a55d3dbbd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -18,11 +18,12 @@
package com.android.wm.shell.apptoweb
+import android.app.assist.AssistContent
+import android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
-import android.content.Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER
import android.content.pm.PackageManager
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationUserState
@@ -58,13 +59,15 @@ fun isBrowserApp(context: Context, packageName: String, userId: Int): Boolean {
* Returns intent if there is a browser application available to handle the uri. Otherwise, returns
* null.
*/
-fun getBrowserIntent(uri: Uri, packageManager: PackageManager): Intent? {
+fun getBrowserIntent(uri: Uri, packageManager: PackageManager, userId: Int): Intent? {
val intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER)
.setData(uri)
.addFlags(FLAG_ACTIVITY_NEW_TASK)
- // If there is no browser application available to handle intent, return null
- val component = intent.resolveActivity(packageManager) ?: return null
- intent.setComponent(component)
+ // If there is a browser application available to handle the intent, return the intent.
+ // Otherwise, return null.
+ val resolveInfo = packageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId)
+ ?: return null
+ intent.setComponent(resolveInfo.componentInfo.componentName)
return intent
}
@@ -72,14 +75,17 @@ fun getBrowserIntent(uri: Uri, packageManager: PackageManager): Intent? {
* Returns intent if there is a non-browser application available to handle the uri. Otherwise,
* returns null.
*/
-fun getAppIntent(uri: Uri, packageManager: PackageManager): Intent? {
- val intent = Intent(ACTION_VIEW, uri).apply {
- flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
+fun getAppIntent(uri: Uri, packageManager: PackageManager, userId: Int): Intent? {
+ val intent = Intent(ACTION_VIEW, uri).addFlags(FLAG_ACTIVITY_NEW_TASK)
+ val resolveInfo = packageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId)
+ ?: return null
+ // If there is a non-browser application available to handle the intent, return the intent.
+ // Otherwise, return null.
+ if (resolveInfo.activityInfo != null && !resolveInfo.handleAllWebDataURI) {
+ intent.setComponent(resolveInfo.componentInfo.componentName)
+ return intent
}
- // If there is no application available to handle intent, return null
- val component = intent.resolveActivity(packageManager) ?: return null
- intent.setComponent(component)
- return intent
+ return null
}
/**
@@ -102,3 +108,10 @@ fun getDomainVerificationUserState(
return null
}
}
+
+/**
+ * Returns the web uri from the given [AssistContent].
+ */
+fun AssistContent.getSessionWebUri(): Uri? {
+ return extras.getParcelable(EXTRA_SESSION_TRANSFER_WEB_URI) ?: webUri
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
index a727b54b3a3f..4cc81a9e6f8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.apptoweb
import android.app.ActivityManager.RunningTaskInfo
-import android.app.TaskInfo
import android.content.Context
import android.content.pm.verify.domain.DomainVerificationManager
import android.graphics.Bitmap
@@ -36,8 +35,17 @@ import android.widget.TextView
import android.window.TaskConstants
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
/**
@@ -45,13 +53,14 @@ import java.util.function.Supplier
*/
internal class OpenByDefaultDialog(
private val context: Context,
- private val taskInfo: TaskInfo,
+ private val taskInfo: RunningTaskInfo,
private val taskSurface: SurfaceControl,
private val displayController: DisplayController,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
+ @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
private val listener: DialogLifecycleListener,
- appIconBitmap: Bitmap?,
- appName: CharSequence?
) {
private lateinit var dialog: OpenByDefaultDialogView
private lateinit var viewHost: SurfaceControlViewHost
@@ -67,11 +76,20 @@ internal class OpenByDefaultDialog(
context.getSystemService(DomainVerificationManager::class.java)!!
private val packageName = taskInfo.baseActivity?.packageName!!
+ private var loadAppInfoJob: Job? = null
init {
createDialog()
initializeRadioButtons()
- bindAppInfo(appIconBitmap, appName)
+ loadAppInfoJob = bgScope.launch {
+ if (!isActive) return@launch
+ val name = taskResourceLoader.getName(taskInfo)
+ val icon = taskResourceLoader.getHeaderIcon(taskInfo)
+ withContext(mainDispatcher.immediate) {
+ if (!isActive) return@withContext
+ bindAppInfo(icon, name)
+ }
+ }
}
/** Creates an open by default settings dialog. */
@@ -147,14 +165,15 @@ internal class OpenByDefaultDialog(
}
private fun closeMenu() {
+ loadAppInfoJob?.cancel()
dialogContainer?.releaseView()
dialogContainer = null
listener.onDialogDismissed()
}
private fun bindAppInfo(
- appIconBitmap: Bitmap?,
- appName: CharSequence?
+ appIconBitmap: Bitmap,
+ appName: CharSequence
) {
appIconView.setImageBitmap(appIconBitmap)
appNameView.text = appName
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
new file mode 100644
index 000000000000..84596b015209
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
@@ -0,0 +1,6 @@
+# WM shell sub-module automotive owners
+
+winsonc@google.com
+stenning@google.com
+gauravbhola@google.com
+xiangw@google.com \ No newline at end of file
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 c024840498ad..ddc107e0dbc4 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
@@ -214,6 +214,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
setTriggerBack(false);
+ // Trigger close transition if necessary.
+ if (Flags.migratePredictiveBackTransition()) {
+ mBackTransitionHandler.onAnimationFinished();
+ }
resetTouchTracker();
// Don't wait for animation start
mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
@@ -731,6 +735,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
callback.onBackStarted(backEvent);
if (mBackTransitionHandler.canHandOffAnimation()) {
callback.setHandoffHandler(mHandoffHandler);
+ } else {
+ callback.setHandoffHandler(null);
}
mOnBackStartDispatched = true;
} catch (RemoteException e) {
@@ -1100,11 +1106,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mCurrentTracker.updateStartLocation();
BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
dispatchOnBackStarted(mActiveCallback, startEvent);
- // TODO(b/373544911): onBackStarted is dispatched here so that
- // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts touch
- // events while it's active. It would be cleaner and safer to disable multitouch
- // altogether (same as in gesture-nav).
- dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
+ if (startEvent.getSwipeEdge() == EDGE_NONE) {
+ // TODO(b/373544911): onBackStarted is dispatched here so that
+ // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts
+ // touch events while it's active. It would be cleaner and safer to disable
+ // multitouch altogether (same as in gesture-nav).
+ dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
+ }
}
}
@@ -1273,14 +1281,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- final boolean isPrepareTransition =
- info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
- if (isPrepareTransition) {
- if (checkTakeoverFlags()) {
- mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
- }
- kickStartAnimation();
- }
// Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
// need to post to ShellExecutor when called.
if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
@@ -1308,14 +1308,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// animation never start, consume directly
applyAndFinish(st, ft, finishCallback);
return true;
- } else if (mClosePrepareTransition == null && isPrepareTransition) {
+ } else if (mClosePrepareTransition == null
+ && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
// Gesture animation was cancelled before prepare transition ready, create
// the close prepare transition
createClosePrepareTransition();
}
}
- if (handlePrepareTransition(info, st, ft, finishCallback)) {
+ if (handlePrepareTransition(transition, info, st, ft, finishCallback)) {
+ if (checkTakeoverFlags()) {
+ mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
+ }
+ kickStartAnimation();
return true;
}
return handleCloseTransition(info, st, ft, finishCallback);
@@ -1627,7 +1632,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* happen when core make an activity become visible.
*/
@VisibleForTesting
- boolean handlePrepareTransition(
+ boolean handlePrepareTransition(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@@ -1675,6 +1680,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
st.apply();
+ // In case other transition handler took the handleRequest before this class.
+ mPrepareOpenTransition = transition;
mFinishOpenTransaction = ft;
mFinishOpenTransitionCallback = finishCallback;
mOpenTransitionInfo = info;
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 bec73a1500a7..9aba3aaa3268 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
@@ -2072,10 +2072,7 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
- if (mLayerView != null) {
- // TODO (b/273316505) handle suppression changes, although might not need to
- // to do anything on the layerview side for this...
- }
+ // Nothing to do for our views, handled by launcher / in the bubble bar.
}
@Override
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 52955267a501..47032fd8616f 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
@@ -38,7 +38,6 @@ import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -543,15 +542,15 @@ public class BubbleExpandedView extends LinearLayout {
void applyThemeAttrs() {
final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.dialogCornerRadius,
- com.android.internal.R.attr.materialColorSurfaceBright,
- com.android.internal.R.attr.materialColorSurfaceContainerHigh});
+ android.R.attr.dialogCornerRadius});
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources());
mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
- mBackgroundColorFloating = ta.getColor(1, Color.WHITE);
+ mBackgroundColorFloating = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceBright);
mExpandedViewContainer.setBackgroundColor(mBackgroundColorFloating);
- final int manageMenuBg = ta.getColor(2, Color.WHITE);
+ final int manageMenuBg = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
ta.recycle();
if (mManageButton != null) {
mManageButton.getBackground().setColorFilter(manageMenuBg, PorterDuff.Mode.SRC_IN);
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 1711dca4a8a3..da6948d947d8 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
@@ -28,7 +28,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -209,7 +208,7 @@ public class BubbleFlyoutView extends FrameLayout {
mPointerSize, mPointerSize, false /* isPointingLeft */));
mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
- applyConfigurationColors(getResources().getConfiguration());
+ applyConfigurationColors();
}
@Override
@@ -440,29 +439,23 @@ public class BubbleFlyoutView extends FrameLayout {
boolean flagsChanged = nightModeFlags != mNightModeFlags;
if (flagsChanged) {
mNightModeFlags = nightModeFlags;
- applyConfigurationColors(configuration);
+ applyConfigurationColors();
}
return flagsChanged;
}
- private void applyConfigurationColors(Configuration configuration) {
- int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
- boolean isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
- try (TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{
- com.android.internal.R.attr.materialColorSurfaceContainer,
- com.android.internal.R.attr.materialColorOnSurface,
- com.android.internal.R.attr.materialColorOnSurfaceVariant})) {
- mFloatingBackgroundColor = ta.getColor(0,
- isNightModeOn ? Color.BLACK : Color.WHITE);
- mSenderText.setTextColor(ta.getColor(1,
- isNightModeOn ? Color.WHITE : Color.BLACK));
- mMessageText.setTextColor(ta.getColor(2,
- isNightModeOn ? Color.WHITE : Color.BLACK));
- mBgPaint.setColor(mFloatingBackgroundColor);
- mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
- mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
- }
+ private void applyConfigurationColors() {
+ mFloatingBackgroundColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
+ mSenderText.setTextColor(
+ mContext.getColor(com.android.internal.R.color.materialColorOnSurface));
+ mMessageText.setTextColor(
+ mContext.getColor(com.android.internal.R.color.materialColorOnSurfaceVariant));
+
+ mBgPaint.setColor(mFloatingBackgroundColor);
+ mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index c74412b825d9..862906a11424 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -19,7 +19,6 @@ package com.android.wm.shell.bubbles
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.Context
import android.graphics.Bitmap
-import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Path
import android.graphics.drawable.AdaptiveIconDrawable
@@ -117,18 +116,8 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
val res = context.resources
// Set overflow button accent color, dot color
-
- val typedArray =
- context.obtainStyledAttributes(
- intArrayOf(
- com.android.internal.R.attr.materialColorPrimaryFixed,
- com.android.internal.R.attr.materialColorOnPrimaryFixed
- )
- )
-
- val colorAccent = typedArray.getColor(0, Color.WHITE)
- val shapeColor = typedArray.getColor(1, Color.BLACK)
- typedArray.recycle()
+ val colorAccent = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
+ val shapeColor = context.getColor(com.android.internal.R.color.materialColorOnPrimaryFixed)
dotColor = colorAccent
overflowBtn?.iconDrawable?.setTint(shapeColor)
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 bf98ef82b475..64f54b8ab5be 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
@@ -226,13 +226,11 @@ public class BubbleOverflowContainerView extends LinearLayout {
? res.getColor(R.color.bubbles_dark)
: res.getColor(R.color.bubbles_light));
- final TypedArray typedArray = getContext().obtainStyledAttributes(new int[] {
- com.android.internal.R.attr.materialColorSurfaceBright,
- com.android.internal.R.attr.materialColorOnSurface});
- int bgColor = typedArray.getColor(0, isNightMode ? Color.BLACK : Color.WHITE);
- int textColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, isNightMode);
- typedArray.recycle();
+
+ int bgColor = getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceBright);
+ int textColor = getContext().getColor(com.android.internal.R.color.materialColorOnSurface);
+
setBackgroundColor(bgColor);
mEmptyStateTitle.setTextColor(textColor);
mEmptyStateSubtitle.setTextColor(textColor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
index 9b3054e9ee13..a65466f71861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -15,7 +15,6 @@
*/
package com.android.wm.shell.bubbles
-import android.graphics.Color
import com.android.wm.shell.R
import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -27,7 +26,6 @@ fun BubblePopupView.setup() {
val attrs =
context.obtainStyledAttributes(
intArrayOf(
- com.android.internal.R.attr.materialColorSurfaceContainer,
android.R.attr.dialogCornerRadius
)
)
@@ -35,8 +33,8 @@ fun BubblePopupView.setup() {
val res = context.resources
val config =
BubblePopupDrawable.Config(
- color = attrs.getColor(0, Color.WHITE),
- cornerRadius = attrs.getDimension(1, 0f),
+ color = context.getColor(com.android.internal.R.color.materialColorSurfaceContainer),
+ cornerRadius = attrs.getDimension(0, 0f),
contentPadding = res.getDimensionPixelSize(R.dimen.bubble_popup_padding),
arrowWidth = res.getDimension(R.dimen.bubble_popup_arrow_width),
arrowHeight = res.getDimension(R.dimen.bubble_popup_arrow_height),
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 0fd4206c0545..de85d9af127d 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
@@ -163,8 +163,11 @@ public class BubblePositioner {
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
} else {
- mExpandedViewLargeScreenWidth =
- res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width);
+ int expandedViewLargeScreenSpacing = res.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding);
+ mExpandedViewLargeScreenWidth = Math.min(
+ res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width),
+ bounds.width() - expandedViewLargeScreenSpacing * 2);
}
if (mDeviceConfig.isLargeScreen()) {
if (mDeviceConfig.isSmallTablet()) {
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 88f55b8af8f7..249a218d5e56 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
@@ -40,7 +40,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.PointF;
import android.graphics.PorterDuff;
@@ -1326,10 +1325,9 @@ public class BubbleStackView extends FrameLayout
R.layout.bubble_manage_menu, this, false);
mManageMenu.setVisibility(View.INVISIBLE);
- final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorSurfaceBright});
- final int menuBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
+ final int menuBackgroundColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceBright);
+
mManageMenu.getBackground().setColorFilter(menuBackgroundColor, PorterDuff.Mode.SRC_IN);
PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig);
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 a313bd004a51..3e8a9b64dac6 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.bar;
import static android.view.View.ALPHA;
+import static android.view.View.INVISIBLE;
import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_X;
@@ -25,6 +26,7 @@ import static android.view.View.X;
import static android.view.View.Y;
import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.CORNER_RADIUS;
+import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.TASK_VIEW_ALPHA;
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE;
@@ -32,7 +34,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
@@ -42,11 +43,12 @@ import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
+import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
-import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject.MagneticTarget;
@@ -59,7 +61,7 @@ public class BubbleBarAnimationHelper {
private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
- private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+ private static final int EXPANDED_VIEW_EXPAND_ALPHA_DURATION = 150;
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 400;
private static final int EXPANDED_VIEW_ANIMATE_TO_REST_DURATION = 400;
private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
@@ -72,6 +74,17 @@ public class BubbleBarAnimationHelper {
private static final float DISMISS_VIEW_SCALE = 1.25f;
private static final int HANDLE_ALPHA_ANIMATION_DURATION = 100;
+ private static final float SWITCH_OUT_SCALE = 0.97f;
+ private static final long SWITCH_OUT_SCALE_DURATION = 200L;
+ private static final long SWITCH_OUT_ALPHA_DURATION = 100L;
+ private static final long SWITCH_OUT_HANDLE_ALPHA_DURATION = 50L;
+ private static final long SWITCH_IN_ANIM_DELAY = 50L;
+ private static final long SWITCH_IN_TX_DURATION = 350L;
+ private static final long SWITCH_IN_ALPHA_DURATION = 50L;
+ // Keep this handle alpha delay at least as long as alpha animation for both expanded views.
+ private static final long SWITCH_IN_HANDLE_ALPHA_DELAY = 150L;
+ private static final long SWITCH_IN_HANDLE_ALPHA_DURATION = 100L;
+
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
new PhysicsAnimator.SpringConfig(300f, 0.9f);
@@ -80,68 +93,24 @@ public class BubbleBarAnimationHelper {
private final PhysicsAnimator.SpringConfig mScaleOutSpringConfig =
new PhysicsAnimator.SpringConfig(900f, 1f);
+ private final int mSwitchAnimPositionOffset;
+
/** Matrix used to scale the expanded view container with a given pivot point. */
private final AnimatableScaleMatrix mExpandedViewContainerMatrix = new AnimatableScaleMatrix();
- /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
- private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
-
@Nullable
- private Animator mRunningDragAnimator;
+ private Animator mRunningAnimator;
- private final Context mContext;
- private final BubbleBarLayerView mLayerView;
private final BubblePositioner mPositioner;
private final int[] mTmpLocation = new int[2];
+ // TODO(b/381936992): remove expanded bubble state from this helper class
private BubbleViewProvider mExpandedBubble;
- private boolean mIsExpanded = false;
- public BubbleBarAnimationHelper(Context context,
- BubbleBarLayerView bubbleBarLayerView,
- BubblePositioner positioner) {
- mContext = context;
- mLayerView = bubbleBarLayerView;
+ public BubbleBarAnimationHelper(Context context, BubblePositioner positioner) {
mPositioner = positioner;
-
- mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
- mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
- mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- // We need to be Z ordered on top in order for alpha animations to work.
- bbev.setSurfaceZOrderedOnTop(true);
- bbev.setAnimating(true);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- // The surface needs to be Z ordered on top for alpha values to work on the
- // TaskView, and if we're temporarily hidden, we are still on the screen
- // with alpha = 0f until we animate back. Stay Z ordered on top so the alpha
- // = 0f remains in effect.
- if (mIsExpanded) {
- bbev.setSurfaceZOrderedOnTop(false);
- }
-
- bbev.setContentVisibility(mIsExpanded);
- bbev.setAnimating(false);
- }
- }
- });
- mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- float alpha = (float) valueAnimator.getAnimatedValue();
- bbev.setTaskViewAlpha(alpha);
- bbev.setAlpha(alpha);
- }
- });
+ mSwitchAnimPositionOffset = context.getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_switch_offset);
}
/**
@@ -154,18 +123,11 @@ public class BubbleBarAnimationHelper {
if (bbev == null) {
return;
}
- mIsExpanded = true;
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
- updateExpandedView();
- bbev.setAnimating(true);
- bbev.setSurfaceZOrderedOnTop(true);
- bbev.setContentVisibility(false);
- bbev.setAlpha(0f);
- bbev.setTaskViewAlpha(0f);
- bbev.setVisibility(VISIBLE);
+ prepareForAnimateIn(bbev);
setScaleFromBubbleBar(mExpandedViewContainerMatrix,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT);
@@ -173,7 +135,16 @@ public class BubbleBarAnimationHelper {
bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
bbev.animateExpansionWhenTaskViewVisible(() -> {
- mExpandedViewAlphaAnimator.start();
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ true);
+ alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION);
+ alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ startNewAnimator(alphaAnim);
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
@@ -188,7 +159,7 @@ public class BubbleBarAnimationHelper {
})
.withEndActions(() -> {
bbev.setAnimationMatrix(null);
- updateExpandedView();
+ updateExpandedView(bbev);
if (afterAnimation != null) {
afterAnimation.run();
}
@@ -197,13 +168,24 @@ public class BubbleBarAnimationHelper {
});
}
+ private void prepareForAnimateIn(BubbleBarExpandedView bbev) {
+ bbev.setAnimating(true);
+ updateExpandedView(bbev);
+ // We need to be Z ordered on top in order for taskView alpha to work.
+ // It is also set when the alpha animation starts, but needs to be set here to too avoid
+ // flickers.
+ bbev.setSurfaceZOrderedOnTop(true);
+ bbev.setTaskViewAlpha(0f);
+ bbev.setContentVisibility(false);
+ bbev.setVisibility(VISIBLE);
+ }
+
/**
* Collapses the currently expanded bubble.
*
* @param endRunnable a runnable to run at the end of the animation.
*/
public void animateCollapse(Runnable endRunnable) {
- mIsExpanded = false;
final BubbleBarExpandedView bbev = getExpandedView();
if (bbev == null) {
Log.w(TAG, "Trying to animate collapse without a bubble");
@@ -214,6 +196,19 @@ public class BubbleBarAnimationHelper {
setScaleFromBubbleBar(mExpandedViewContainerMatrix, 1f);
+ bbev.setAnimating(true);
+
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ false);
+ alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION);
+ alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ startNewAnimator(alphaAnim);
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -234,7 +229,6 @@ public class BubbleBarAnimationHelper {
}
})
.start();
- mExpandedViewAlphaAnimator.reverse();
}
private void setScaleFromBubbleBar(AnimatableScaleMatrix matrix, float scale) {
@@ -246,6 +240,143 @@ public class BubbleBarAnimationHelper {
}
/**
+ * Animate between two bubble views using a switch animation
+ *
+ * @param fromBubble bubble to hide
+ * @param toBubble bubble to show
+ * @param afterAnimation optional runnable after animation finishes
+ */
+ public void animateSwitch(BubbleViewProvider fromBubble, BubbleViewProvider toBubble,
+ @Nullable Runnable afterAnimation) {
+ /*
+ * Switch animation
+ *
+ * |.....................fromBubble scale to 0.97.....................|
+ * |fromBubble handle alpha 0|....fromBubble alpha to 0.....| |
+ * 0-------------------------50-----------------------100---150--------200----------250--400
+ * |..toBubble alpha to 1...| |toBubble handle alpha 1| |
+ * |................toBubble position +/-48 to 0...............|
+ */
+
+ mExpandedBubble = toBubble;
+ final BubbleBarExpandedView toBbev = toBubble.getBubbleBarExpandedView();
+ final BubbleBarExpandedView fromBbev = fromBubble.getBubbleBarExpandedView();
+ if (toBbev == null || fromBbev == null) {
+ return;
+ }
+
+ fromBbev.setAnimating(true);
+
+ prepareForAnimateIn(toBbev);
+ final float endTx = toBbev.getTranslationX();
+ final float startTx = getSwitchAnimationInitialTx(endTx);
+ toBbev.setTranslationX(startTx);
+ toBbev.getHandleView().setAlpha(0f);
+ toBbev.getHandleView().setHandleInitialColor(fromBbev.getHandleView().getHandleColor());
+
+ toBbev.animateExpansionWhenTaskViewVisible(() -> {
+ AnimatorSet switchAnim = new AnimatorSet();
+ switchAnim.playTogether(switchOutAnimator(fromBbev), switchInAnimator(toBbev, endTx));
+
+ switchAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (afterAnimation != null) {
+ afterAnimation.run();
+ }
+ }
+ });
+ startNewAnimator(switchAnim);
+ });
+ }
+
+ private float getSwitchAnimationInitialTx(float endTx) {
+ if (mPositioner.isBubbleBarOnLeft()) {
+ return endTx - mSwitchAnimPositionOffset;
+ } else {
+ return endTx + mSwitchAnimPositionOffset;
+ }
+ }
+
+ private Animator switchOutAnimator(BubbleBarExpandedView bbev) {
+ setPivotToCenter(bbev);
+ AnimatorSet scaleAnim = new AnimatorSet();
+ scaleAnim.playTogether(
+ ObjectAnimator.ofFloat(bbev, SCALE_X, SWITCH_OUT_SCALE),
+ ObjectAnimator.ofFloat(bbev, SCALE_Y, SWITCH_OUT_SCALE)
+ );
+ scaleAnim.setInterpolator(Interpolators.ACCELERATE);
+ scaleAnim.setDuration(SWITCH_OUT_SCALE_DURATION);
+
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ false);
+ alphaAnim.setStartDelay(SWITCH_OUT_HANDLE_ALPHA_DURATION);
+ alphaAnim.setDuration(SWITCH_OUT_ALPHA_DURATION);
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 0f)
+ .setDuration(SWITCH_OUT_HANDLE_ALPHA_DURATION);
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playTogether(scaleAnim, alphaAnim, handleAlphaAnim);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ return animator;
+ }
+
+ private Animator switchInAnimator(BubbleBarExpandedView bbev, float restingTx) {
+ ObjectAnimator positionAnim = ObjectAnimator.ofFloat(bbev, TRANSLATION_X, restingTx);
+ positionAnim.setInterpolator(Interpolators.EMPHASIZED_DECELERATE);
+ positionAnim.setStartDelay(SWITCH_IN_ANIM_DELAY);
+ positionAnim.setDuration(SWITCH_IN_TX_DURATION);
+
+ // Animate alpha directly to have finer control over surface z-ordering
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(bbev, TASK_VIEW_ALPHA, 1f);
+ alphaAnim.setStartDelay(SWITCH_IN_ANIM_DELAY);
+ alphaAnim.setDuration(SWITCH_IN_ALPHA_DURATION);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ bbev.setSurfaceZOrderedOnTop(true);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setContentVisibility(true);
+ // The outgoing expanded view alpha animation is still in progress.
+ // Do not reset the surface z-order as otherwise the outgoing expanded view is
+ // placed on top.
+ }
+ });
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 1f);
+ handleAlphaAnim.setStartDelay(SWITCH_IN_HANDLE_ALPHA_DELAY);
+ handleAlphaAnim.setDuration(SWITCH_IN_HANDLE_ALPHA_DURATION);
+ handleAlphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ bbev.setSurfaceZOrderedOnTop(false);
+ bbev.setAnimating(false);
+ }
+ });
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playTogether(positionAnim, alphaAnim, handleAlphaAnim);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ updateExpandedView(bbev);
+ }
+ });
+ return animator;
+ }
+
+
+ /**
* Animate the expanded bubble when it is being dragged
*/
public void animateStartDrag() {
@@ -273,7 +404,7 @@ public class BubbleBarAnimationHelper {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(contentAnim, handleAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -282,7 +413,6 @@ public class BubbleBarAnimationHelper {
* @param endRunnable a runnable to run at the end of the animation
*/
public void animateDismiss(Runnable endRunnable) {
- mIsExpanded = false;
final BubbleBarExpandedView bbev = getExpandedView();
if (bbev == null) {
Log.w(TAG, "Trying to animate dismiss without a bubble");
@@ -335,7 +465,7 @@ public class BubbleBarAnimationHelper {
bbev.setDragging(false);
}
});
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -409,7 +539,7 @@ public class BubbleBarAnimationHelper {
}
}
});
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -435,7 +565,7 @@ public class BubbleBarAnimationHelper {
animatorSet.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION).setInterpolator(
EMPHASIZED_DECELERATE);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -443,14 +573,15 @@ public class BubbleBarAnimationHelper {
*/
public void cancelAnimations() {
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
- mExpandedViewAlphaAnimator.cancel();
BubbleBarExpandedView bbev = getExpandedView();
if (bbev != null) {
bbev.animate().cancel();
}
- if (mRunningDragAnimator != null) {
- mRunningDragAnimator.cancel();
- mRunningDragAnimator = null;
+ if (mRunningAnimator != null) {
+ if (mRunningAnimator.isRunning()) {
+ mRunningAnimator.cancel();
+ }
+ mRunningAnimator = null;
}
}
@@ -462,8 +593,7 @@ public class BubbleBarAnimationHelper {
return null;
}
- private void updateExpandedView() {
- BubbleBarExpandedView bbev = getExpandedView();
+ private void updateExpandedView(BubbleBarExpandedView bbev) {
if (bbev == null) {
Log.w(TAG, "Trying to update the expanded view without a bubble");
return;
@@ -477,6 +607,8 @@ public class BubbleBarAnimationHelper {
bbev.setLayoutParams(lp);
bbev.setX(position.x);
bbev.setY(position.y);
+ bbev.setScaleX(1f);
+ bbev.setScaleY(1f);
bbev.updateLocation();
bbev.maybeShowOverflow();
}
@@ -500,18 +632,54 @@ public class BubbleBarAnimationHelper {
return new Size(width, height);
}
- private void startNewDragAnimation(Animator animator) {
+ private void startNewAnimator(Animator animator) {
cancelAnimations();
- mRunningDragAnimator = animator;
+ mRunningAnimator = animator;
animator.start();
}
+ /**
+ * Animate the alpha of the expanded view between visible (1) and invisible (0).
+ * {@link BubbleBarExpandedView} requires
+ * {@link com.android.wm.shell.bubbles.BubbleExpandedView#setSurfaceZOrderedOnTop(boolean)} to
+ * be called before alpha can be applied.
+ * Only supports alpha of 1 or 0. Otherwise we can't reset surface z-order at the end.
+ */
+ private ObjectAnimator createAlphaAnimator(BubbleBarExpandedView bubbleBarExpandedView,
+ boolean visible) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(bubbleBarExpandedView, TASK_VIEW_ALPHA,
+ visible ? 1f : 0f);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Move task view to the top of the window so alpha can be applied to it
+ bubbleBarExpandedView.setSurfaceZOrderedOnTop(true);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bubbleBarExpandedView.setContentVisibility(visible);
+ if (!visible) {
+ // Hide the expanded view before we reset the z-ordering
+ bubbleBarExpandedView.setVisibility(INVISIBLE);
+ }
+ bubbleBarExpandedView.setSurfaceZOrderedOnTop(false);
+ }
+ });
+ return animator;
+ }
+
private static void setDragPivot(BubbleBarExpandedView bbev) {
bbev.setPivotX(bbev.getWidth() / 2f);
bbev.setPivotY(0f);
}
- private class DragAnimatorListenerAdapter extends AnimatorListenerAdapter {
+ private static void setPivotToCenter(BubbleBarExpandedView bbev) {
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(bbev.getHeight() / 2f);
+ }
+
+ private static class DragAnimatorListenerAdapter extends AnimatorListenerAdapter {
private final BubbleBarExpandedView mBubbleBarExpandedView;
@@ -523,11 +691,9 @@ public class BubbleBarAnimationHelper {
public void onAnimationStart(Animator animation) {
mBubbleBarExpandedView.setAnimating(true);
}
-
@Override
public void onAnimationEnd(Animator animation) {
mBubbleBarExpandedView.setAnimating(false);
- mRunningDragAnimator = null;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index ed49417ad3bd..65c929ab6fb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -85,6 +85,22 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
};
+ /**
+ * Property to set alpha for the task view
+ */
+ public static final FloatProperty<BubbleBarExpandedView> TASK_VIEW_ALPHA = new FloatProperty<>(
+ "taskViewAlpha") {
+ @Override
+ public void setValue(BubbleBarExpandedView bbev, float alpha) {
+ bbev.setTaskViewAlpha(alpha);
+ }
+
+ @Override
+ public Float get(BubbleBarExpandedView bbev) {
+ return bbev.mTaskView != null ? bbev.mTaskView.getAlpha() : bbev.getAlpha();
+ }
+ };
+
private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
private static final int INVALID_TASK_ID = -1;
@@ -590,6 +606,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
}
+ @VisibleForTesting
+ boolean isSurfaceZOrderedOnTop() {
+ return mTaskView != null && mTaskView.isZOrderedOnTop();
+ }
+
/**
* Sets whether the view is animating, in this case we won't change the content visibility
* until the animation is done.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index 712e41b0b3c5..9cf0d2db710b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -65,6 +65,7 @@ public class BubbleBarHandleView extends View {
@Nullable
private ObjectAnimator mColorChangeAnim;
private @ColorInt int mRegionSamplerColor;
+ private boolean mHasSampledColor;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -102,7 +103,11 @@ public class BubbleBarHandleView extends View {
invalidate();
}
- private int getHandleColor() {
+ /**
+ * Get current color value for the handle
+ */
+ @ColorInt
+ public int getHandleColor() {
return mHandlePaint.getColor();
}
@@ -128,6 +133,16 @@ public class BubbleBarHandleView extends View {
}
/**
+ * Set initial color for the handle. Takes effect if the
+ * {@link #updateHandleColor(boolean, boolean)} has not been called.
+ */
+ public void setHandleInitialColor(@ColorInt int color) {
+ if (!mHasSampledColor) {
+ setHandleColor(color);
+ }
+ }
+
+ /**
* Updates the handle color.
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
@@ -139,6 +154,7 @@ public class BubbleBarHandleView extends View {
if (newColor == mRegionSamplerColor) {
return;
}
+ mHasSampledColor = true;
mRegionSamplerColor = newColor;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
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 0c05e3c5115c..425afbed0742 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
@@ -103,8 +103,7 @@ public class BubbleBarLayerView extends FrameLayout
mPositioner = mBubbleController.getPositioner();
mBubbleLogger = bubbleLogger;
- mAnimationHelper = new BubbleBarAnimationHelper(context,
- this, mPositioner);
+ mAnimationHelper = new BubbleBarAnimationHelper(context, mPositioner);
mEducationViewController = new BubbleEducationViewController(context, (boolean visible) -> {
if (mExpandedView == null) return;
mExpandedView.setObscured(visible);
@@ -183,8 +182,14 @@ public class BubbleBarLayerView extends FrameLayout
// Already showing this bubble, skip animating
return;
}
+ BubbleViewProvider previousBubble = null;
if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
- removeView(mExpandedView);
+ if (mIsExpanded) {
+ // Previous expanded view open, keep it visible to animate the switch
+ previousBubble = mExpandedBubble;
+ } else {
+ removeView(mExpandedView);
+ }
mExpandedView = null;
}
if (mExpandedView == null) {
@@ -246,7 +251,8 @@ public class BubbleBarLayerView extends FrameLayout
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
- mAnimationHelper.animateExpansion(mExpandedBubble, () -> {
+
+ final Runnable afterAnimation = () -> {
if (mExpandedView == null) return;
// Touch delegate for the menu
BubbleBarHandleView view = mExpandedView.getHandleView();
@@ -256,7 +262,18 @@ public class BubbleBarLayerView extends FrameLayout
mHandleTouchDelegate = new TouchDelegate(mHandleTouchBounds,
mExpandedView.getHandleView());
setTouchDelegate(mHandleTouchDelegate);
- });
+ };
+
+ if (previousBubble != null) {
+ final BubbleBarExpandedView previousExpandedView =
+ previousBubble.getBubbleBarExpandedView();
+ mAnimationHelper.animateSwitch(previousBubble, mExpandedBubble, () -> {
+ removeView(previousExpandedView);
+ afterAnimation.run();
+ });
+ } else {
+ mAnimationHelper.animateExpansion(mExpandedBubble, afterAnimation);
+ }
showScrim(true);
}
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 1c71ef415eae..6c14d83dfafa 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
@@ -17,7 +17,6 @@ package com.android.wm.shell.bubbles.bar;
import android.annotation.ColorInt;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
@@ -63,9 +62,8 @@ public class BubbleBarMenuItemView extends LinearLayout {
*/
void update(Icon icon, String title, @ColorInt int tint) {
if (tint == Color.TRANSPARENT) {
- final TypedArray typedArray = getContext().obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.materialColorOnSurface});
- mTextView.setTextColor(typedArray.getColor(0, Color.BLACK));
+ mTextView.setTextColor(
+ getContext().getColor(com.android.internal.R.color.materialColorOnSurface));
} else {
icon.setTint(tint);
mTextView.setTextColor(tint);
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 99e20097e61c..dfbf655bb6fc 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
@@ -18,7 +18,6 @@ package com.android.wm.shell.bubbles.bar;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
@@ -91,14 +90,11 @@ public class BubbleBarMenuView extends LinearLayout {
}
private void updateThemeColors() {
- try (TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorSurfaceBright,
- com.android.internal.R.attr.materialColorOnSurface
- })) {
- mActionsSectionView.getBackground().setTint(ta.getColor(0, Color.WHITE));
- ImageViewCompat.setImageTintList(mBubbleDismissIconView,
- ColorStateList.valueOf(ta.getColor(1, Color.BLACK)));
- }
+ mActionsSectionView.getBackground().setTint(
+ mContext.getColor(com.android.internal.R.color.materialColorSurfaceBright));
+ ImageViewCompat.setImageTintList(mBubbleDismissIconView,
+ ColorStateList.valueOf(
+ mContext.getColor(com.android.internal.R.color.materialColorOnSurface)));
}
/** Animates the menu from the specified start scale. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 9dd0cae20370..5f437d4af40f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -22,8 +22,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.view.LayoutInflater;
import android.view.View;
@@ -169,12 +167,7 @@ class BubbleBarMenuViewController {
int handleHeight = mHandleView.getHandleHeight();
float targetWidth = mHandleView.getHandleWidth() + widthDiff * WIDTH_SWAP_FRACTION;
float targetHeight = targetWidth * mMenuView.getTitleItemHeight() / mMenuView.getWidth();
- int menuColor;
- try (TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorSurfaceBright,
- })) {
- menuColor = ta.getColor(0, Color.WHITE);
- }
+ int menuColor = mContext.getColor(com.android.internal.R.color.materialColorSurfaceBright);
// Calculating deltas
float swapScale = targetWidth / mMenuView.getWidth();
float handleWidthDelta = targetWidth - mHandleView.getHandleWidth();
@@ -227,11 +220,8 @@ class BubbleBarMenuViewController {
private ArrayList<BubbleBarMenuView.MenuAction> createMenuActions(Bubble bubble) {
ArrayList<BubbleBarMenuView.MenuAction> menuActions = new ArrayList<>();
Resources resources = mContext.getResources();
- int tintColor;
- try (TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorOnSurface})) {
- tintColor = ta.getColor(0, Color.TRANSPARENT);
- }
+ int tintColor = mContext.getColor(com.android.internal.R.color.materialColorOnSurface);
+
if (bubble.isConversation()) {
// Don't bubble conversation action
menuActions.add(new BubbleBarMenuView.MenuAction(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt
new file mode 100644
index 000000000000..498d0e406e4b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.common
+
+import android.os.Looper
+import java.util.concurrent.Executor
+
+/** Executor implementation which can be boosted temporarily to a different thread priority. */
+interface BoostExecutor : Executor {
+ /**
+ * Requests that the executor is boosted until {@link #resetBoost()} is called.
+ */
+ fun setBoost() {}
+
+ /**
+ * Requests that the executor is not boosted (only resets if there are no other boost requests
+ * in progress).
+ */
+ fun resetBoost() {}
+
+ /**
+ * Returns whether the executor is boosted.
+ */
+ fun isBoosted() : Boolean {
+ return false
+ }
+
+ /**
+ * Returns the looper for this executor.
+ */
+ fun getLooper() : Looper? {
+ return Looper.myLooper()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 38b859220256..c74bf53268f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -228,7 +228,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
- @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
boolean mImeRequestedVisible =
(WindowInsets.Type.defaultVisible() & WindowInsets.Type.ime()) != 0;
InsetsSourceControl mImeSourceControl = null;
@@ -339,6 +338,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// Make mImeSourceControl point to the new control before starting the animation.
if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
mImeSourceControl.release(SurfaceControl::release);
+ if (android.view.inputmethod.Flags.refactorInsetsController()
+ && !hasImeLeash && mAnimation != null) {
+ // In case of losing the leash, the animation should be cancelled.
+ mAnimation.cancel();
+ }
}
mImeSourceControl = imeSourceControl;
@@ -415,9 +419,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// already (e.g., when focussing an editText in activity B, while and editText in
// activity A is focussed), we will not get a call of #insetsControlChanged, and
// therefore have to start the show animation from here
- startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */);
+ startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */,
+ statsToken);
- setVisibleDirectly(mImeRequestedVisible || mAnimation != null, statsToken);
+ // In case of a hide, the statsToken should not been send yet (as the animation
+ // is still ongoing). It will be sent at the end of the animation
+ boolean hideAnimOngoing = !mImeRequestedVisible && mAnimation != null;
+ setVisibleDirectly(mImeRequestedVisible || mAnimation != null,
+ hideAnimOngoing ? null : statsToken);
}
}
@@ -426,12 +435,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*/
private void setVisibleDirectly(boolean visible, @Nullable ImeTracker.Token statsToken) {
mInsetsState.setSourceVisible(InsetsSource.ID_IME, visible);
- mRequestedVisibleTypes = visible
- ? mRequestedVisibleTypes | WindowInsets.Type.ime()
- : mRequestedVisibleTypes & ~WindowInsets.Type.ime();
+ int visibleTypes = visible ? WindowInsets.Type.ime() : 0;
try {
mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId,
- mRequestedVisibleTypes, statsToken);
+ visibleTypes, WindowInsets.Type.ime(), statsToken);
} catch (RemoteException e) {
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index 736d954513b1..803f16ce39c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -16,15 +16,50 @@
package com.android.wm.shell.common;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
+import static android.os.Process.setThreadPriority;
+
import android.annotation.NonNull;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.function.BiConsumer;
/** Executor implementation which is backed by a Handler. */
public class HandlerExecutor implements ShellExecutor {
+ @NonNull
private final Handler mHandler;
+ // See android.os.Process#THREAD_PRIORITY_*
+ private final int mDefaultThreadPriority;
+ private final int mBoostedThreadPriority;
+ // Number of current requests to boost thread priority
+ private int mBoostCount;
+ private final Object mBoostLock = new Object();
+ // Default function for setting thread priority (tid, priority)
+ private BiConsumer<Integer, Integer> mSetThreadPriorityFn =
+ HandlerExecutor::setThreadPriorityInternal;
public HandlerExecutor(@NonNull Handler handler) {
+ this(handler, THREAD_PRIORITY_DEFAULT, THREAD_PRIORITY_DEFAULT);
+ }
+
+ /**
+ * Used only if this executor can be boosted, if so, it can be boosted to the given
+ * {@param boostPriority}.
+ */
+ public HandlerExecutor(@NonNull Handler handler, int defaultThreadPriority,
+ int boostedThreadPriority) {
mHandler = handler;
+ mDefaultThreadPriority = defaultThreadPriority;
+ mBoostedThreadPriority = boostedThreadPriority;
+ }
+
+ @VisibleForTesting
+ void replaceSetThreadPriorityFn(BiConsumer<Integer, Integer> setThreadPriorityFn) {
+ mSetThreadPriorityFn = setThreadPriorityFn;
}
@Override
@@ -56,9 +91,54 @@ public class HandlerExecutor implements ShellExecutor {
}
@Override
+ public void setBoost() {
+ synchronized (mBoostLock) {
+ if (mDefaultThreadPriority == mBoostedThreadPriority) {
+ // Nothing to boost
+ return;
+ }
+ if (mBoostCount == 0) {
+ mSetThreadPriorityFn.accept(
+ ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
+ mBoostedThreadPriority);
+ }
+ mBoostCount++;
+ }
+ }
+
+ @Override
+ public void resetBoost() {
+ synchronized (mBoostLock) {
+ mBoostCount--;
+ if (mBoostCount == 0) {
+ mSetThreadPriorityFn.accept(
+ ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
+ mDefaultThreadPriority);
+ }
+ }
+ }
+
+ @Override
+ public boolean isBoosted() {
+ synchronized (mBoostLock) {
+ return mBoostCount > 0;
+ }
+ }
+
+ @Override
+ @NonNull
+ public Looper getLooper() {
+ return mHandler.getLooper();
+ }
+
+ @Override
public void assertCurrentThread() {
if (!mHandler.getLooper().isCurrentThread()) {
throw new IllegalStateException("must be called on " + mHandler);
}
}
+
+ private static void setThreadPriorityInternal(Integer tid, Integer priority) {
+ setThreadPriority(tid, priority);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index 2c2961fd4b65..9e5071e8347b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -18,15 +18,15 @@ package com.android.wm.shell.common;
import java.lang.reflect.Array;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* Super basic Executor interface that adds support for delayed execution and removing callbacks.
- * Intended to wrap Handler while better-supporting testing.
+ * Intended to wrap Handler while better-supporting testing. Not every ShellExecutor implementation
+ * may support boosting.
*/
-public interface ShellExecutor extends Executor {
+public interface ShellExecutor extends BoostExecutor {
/**
* Executes the given runnable. If the caller is running on the same looper as this executor,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index 193c593e2ab2..2c4df0c0ca49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -25,7 +25,6 @@ import com.android.wm.shell.common.ShellExecutor
class PipAppOpsListener(
private val mContext: Context,
- private val mCallback: Callback,
private val mMainExecutor: ShellExecutor
) {
private val mAppOpsManager: AppOpsManager = checkNotNull(
@@ -46,7 +45,9 @@ class PipAppOpsListener(
packageName
) != AppOpsManager.MODE_ALLOWED
) {
- mMainExecutor.execute { mCallback.dismissPip() }
+ mCallback?.let {
+ mMainExecutor.execute { it.dismissPip() }
+ }
}
} catch (e: PackageManager.NameNotFoundException) {
// Unregister the listener if the package can't be found
@@ -54,6 +55,12 @@ class PipAppOpsListener(
}
}
+ private var mCallback: Callback? = null
+
+ fun setCallback(callback: Callback) {
+ mCallback = callback
+ }
+
fun onActivityPinned(packageName: String) {
// Register for changes to the app ops setting for this package while it is in PiP
registerAppOpsListener(packageName)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 813772f20a8a..2f5afcaa907b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -352,8 +352,8 @@ public class DividerSnapAlgorithm {
? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;
float ratio = areOffscreenRatiosSupported()
- ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
- : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
+ ? SplitSpec.OFFSCREEN_ASYMMETRIC_RATIO
+ : SplitSpec.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
int size = (int) (ratio * (end - start)) - mDividerSize / 2;
int leftTopPosition = start + pinnedTaskbarShiftStart + size;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index d20ad5d1b908..9fb36b36ff29 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -33,12 +33,14 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Binder;
+import android.os.Trace;
import android.view.IWindow;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -50,10 +52,12 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ScreenshotUtils;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SurfaceUtils;
import java.util.function.Consumer;
@@ -79,9 +83,19 @@ public class SplitDecorManager extends WindowlessWindowManager {
private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
+ // Indicates the loading state of mIcon
+ enum IconLoadState {
+ NOT_LOADED,
+ LOADING,
+ LOADED
+ }
+
private final IconProvider mIconProvider;
+ private final ShellExecutor mMainExecutor;
+ private final ShellExecutor mBgExecutor;
private Drawable mIcon;
+ private IconLoadState mIconLoadState = IconLoadState.NOT_LOADED;
private ImageView mVeilIconView;
private SurfaceControlViewHost mViewHost;
/** The parent surface that this is attached to. Should be the stage root. */
@@ -109,9 +123,14 @@ public class SplitDecorManager extends WindowlessWindowManager {
private int mOffsetY;
private int mRunningAnimationCount = 0;
- public SplitDecorManager(Configuration configuration, IconProvider iconProvider) {
+ public SplitDecorManager(Configuration configuration,
+ IconProvider iconProvider,
+ ShellExecutor mainExecutor,
+ ShellExecutor bgExecutor) {
super(configuration, null /* rootSurface */, null /* hostInputToken */);
mIconProvider = iconProvider;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
}
@Override
@@ -199,6 +218,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
mHostLeash = null;
mIcon = null;
+ mIconLoadState = IconLoadState.NOT_LOADED;
mVeilIconView = null;
mIsCurrentlyChanging = false;
mShown = false;
@@ -260,10 +280,11 @@ public class SplitDecorManager extends WindowlessWindowManager {
.setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
}
- if (mIcon == null && resizingTask.topActivityInfo != null) {
- mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
- mVeilIconView.setImageDrawable(mIcon);
- mVeilIconView.setVisibility(View.VISIBLE);
+ if (mIconLoadState == IconLoadState.NOT_LOADED && resizingTask.topActivityInfo != null) {
+ loadIconInBackground(resizingTask.topActivityInfo, () -> {
+ mVeilIconView.setImageDrawable(mIcon);
+ mVeilIconView.setVisibility(View.VISIBLE);
+ });
WindowManager.LayoutParams lp =
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -417,10 +438,10 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
if (mIcon == null && resizingTask.topActivityInfo != null) {
- // Initialize icon
- mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
- mVeilIconView.setImageDrawable(mIcon);
- mVeilIconView.setVisibility(View.VISIBLE);
+ loadIconInBackground(resizingTask.topActivityInfo, () -> {
+ mVeilIconView.setImageDrawable(mIcon);
+ mVeilIconView.setVisibility(View.VISIBLE);
+ });
WindowManager.LayoutParams lp =
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -453,7 +474,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
return;
}
- // Recenter icon
+ // Re-center icon
t.setPosition(mIconLeash,
mInstantaneousBounds.width() / 2f - mIconSize / 2f,
mInstantaneousBounds.height() / 2f - mIconSize / 2f);
@@ -596,9 +617,38 @@ public class SplitDecorManager extends WindowlessWindowManager {
mVeilIconView.setImageDrawable(null);
t.hide(mIconLeash);
mIcon = null;
+ mIconLoadState = IconLoadState.NOT_LOADED;
}
}
+ /**
+ * Loads the icon for the given {@param info}, calling {@param postLoadCb} on the main thread
+ * if provided.
+ */
+ private void loadIconInBackground(@NonNull ActivityInfo info, @Nullable Runnable postLoadCb) {
+ mIconLoadState = IconLoadState.LOADING;
+ mBgExecutor.setBoost();
+ mBgExecutor.execute(() -> {
+ Trace.beginSection("SplitDecorManager.loadIconInBackground("
+ + info.applicationInfo.packageName + ")");
+ final Drawable icon = mIconProvider.getIcon(info);
+ Trace.endSection();
+ mMainExecutor.execute(() -> {
+ if (mIconLoadState != IconLoadState.LOADING) {
+ // The request was canceled while loading in the background, just drop the
+ // result
+ return;
+ }
+ mIcon = icon;
+ mIconLoadState = IconLoadState.LOADED;
+ if (postLoadCb != null) {
+ postLoadCb.run();
+ }
+ });
+ mBgExecutor.resetBoost();
+ });
+ }
+
private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
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 1852cda7e804..21c44c9b92ee 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
@@ -112,11 +112,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private static final int FLING_EXIT_DURATION = 450;
private static final int FLING_OFFSCREEN_DURATION = 500;
- /** A split ratio used on larger screens, where we can fit both apps onscreen. */
- public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f;
- /** A split ratio used on smaller screens, where we place one app mostly offscreen. */
- public static final float OFFSCREEN_ASYMMETRIC_RATIO = 0.1f;
-
// Here are some (arbitrarily decided) layer definitions used during animations to make sure the
// layers stay in order. (During transitions, everything is reparented onto a transition root
// and can be freely relayered.)
@@ -236,7 +231,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
updateDividerConfig(mContext);
mRootBounds.set(configuration.windowConfiguration.getBounds());
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ updateLayouts();
mInteractionJankMonitor = InteractionJankMonitor.getInstance();
resetDividerPosition();
updateInvisibleRect();
@@ -490,7 +485,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mIsLargeScreen = configuration.smallestScreenWidthDp >= 600;
mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
configuration);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ updateLayouts();
updateDividerConfig(mContext);
initDividerPosition(mTempRect, wasLeftRightSplit);
updateInvisibleRect();
@@ -518,7 +513,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mRootBounds.set(tmpRect);
mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
mIsLargeScreen, mRootBounds.width() >= mRootBounds.height());
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ updateLayouts();
initDividerPosition(mTempRect, wasLeftRightSplit);
}
@@ -652,7 +647,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
if (!mPinnedTaskbarInsets.equals(pinnedTaskbarInsets)) {
mPinnedTaskbarInsets = pinnedTaskbarInsets;
// Refresh the DividerSnapAlgorithm.
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ updateLayouts();
// If the divider is no longer placed on a snap point, animate it to the nearest one.
DividerSnapAlgorithm.SnapTarget snapTarget =
findSnapTarget(mDividerPosition, 0, false /* hardDismiss */);
@@ -799,6 +794,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
void onStartDragging() {
+ // This triggers initialization of things like the resize veil in preparation for
+ // showing it when the user moves the divider past the slop
+ updateDividerBounds(getDividerPosition(), false /* shouldUseParallaxEffect */);
+
mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler,
CUJ_SPLIT_SCREEN_RESIZE);
}
@@ -824,8 +823,22 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
}
- private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
- final Rect insets = getDisplayStableInsets(context);
+ /**
+ * (Re)calculates the split screen logic for this particular display/orientation. Refreshes the
+ * DividerSnapAlgorithm, which controls divider snap points, and populates a map in SplitState
+ * with bounds for all valid split layouts.
+ */
+ private void updateLayouts() {
+ // Update SplitState map
+
+ if (Flags.enableFlexibleTwoAppSplit()) {
+ mSplitState.populateLayouts(
+ mRootBounds, mDividerSize, mIsLeftRightSplit, mPinnedTaskbarInsets.toRect());
+ }
+
+ // Get new DividerSnapAlgorithm
+
+ final Rect insets = getDisplayStableInsets(mContext);
// Make split axis insets value same as the larger one to avoid bounds1 and bounds2
// have difference for avoiding size-compat mode when switching unresizable apps in
@@ -835,10 +848,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
insets.set(insets.left, largerInsets, insets.right, largerInsets);
}
- return new DividerSnapAlgorithm(
- context.getResources(),
- rootBounds.width(),
- rootBounds.height(),
+ mDividerSnapAlgorithm = new DividerSnapAlgorithm(
+ mContext.getResources(),
+ mRootBounds.width(),
+ mRootBounds.height(),
mDividerSize,
mIsLeftRightSplit,
insets,
@@ -1560,7 +1573,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
final int imeTargetPosition = getImeTargetPosition();
mHasImeFocus = imeTargetPosition != SPLIT_POSITION_UNDEFINED;
if (!mHasImeFocus) {
- return 0;
+ if (!android.view.inputmethod.Flags.refactorInsetsController() || showing) {
+ return 0;
+ }
}
mStartImeTop = showing ? hiddenTop : shownTop;
@@ -1613,7 +1628,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
- if (displayId != mDisplayId || !mHasImeFocus) return;
+ if (displayId != mDisplayId || !mHasImeFocus) {
+ if (!android.view.inputmethod.Flags.refactorInsetsController() || mImeShown) {
+ return;
+ }
+ }
onProgress(getProgress(imeTop));
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
@@ -1621,7 +1640,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {
- if (displayId != mDisplayId || !mHasImeFocus || cancel) return;
+ if (displayId != mDisplayId || cancel) return;
+ if (!mHasImeFocus) {
+ if (!android.view.inputmethod.Flags.refactorInsetsController() || mImeShown) {
+ return;
+ }
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Split IME animation ending, canceled=%b", cancel);
onProgress(1.0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
new file mode 100644
index 000000000000..9c951bd89876
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
@@ -0,0 +1,183 @@
+/*
+ * 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.common.split;
+
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_33_33_33;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_45_45_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.stateToString;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitScreenState;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A reference class that stores the split layouts available in this device/orientation. Layouts are
+ * available as lists of RectFs, where each RectF represents the bounds of an app.
+ */
+public class SplitSpec {
+ private static final String TAG = "SplitSpec";
+ private static final boolean DEBUG = true;
+
+ /** A split ratio used on larger screens, where we can fit both apps onscreen. */
+ public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f;
+ /** A split ratio used on smaller screens, where we place one app mostly offscreen. */
+ public static final float OFFSCREEN_ASYMMETRIC_RATIO = 0.1f;
+ /** A 50-50 split ratio. */
+ public static final float MIDDLE_RATIO = 0.5f;
+
+ private final boolean mIsLeftRightSplit;
+ /** The usable display area, considering insets that affect split bounds. */
+ private final RectF mUsableArea;
+ /** Half the divider size. */
+ private final float mHalfDiv;
+
+ /** A large map that stores all valid split layouts. */
+ private final Map<Integer, List<RectF>> mLayouts = new HashMap<>();
+
+ /** Constructor; initializes the layout map. */
+ public SplitSpec(Rect displayBounds, int dividerSize, boolean isLeftRightSplit,
+ Rect pinnedTaskbarInsets) {
+ mIsLeftRightSplit = isLeftRightSplit;
+ mUsableArea = new RectF(displayBounds);
+ mUsableArea.left += pinnedTaskbarInsets.left;
+ mUsableArea.top += pinnedTaskbarInsets.top;
+ mUsableArea.right -= pinnedTaskbarInsets.right;
+ mUsableArea.bottom -= pinnedTaskbarInsets.bottom;
+ mHalfDiv = dividerSize / 2f;
+
+ // The "start" position, considering insets.
+ float s = isLeftRightSplit ? mUsableArea.left : mUsableArea.top;
+ // The "end" position, considering insets.
+ float e = isLeftRightSplit ? mUsableArea.right : mUsableArea.bottom;
+ // The "length" of the usable display (width or height). Apps are arranged along this axis.
+ float l = e - s;
+ float divPos;
+ float divPos2;
+
+ // SNAP_TO_2_10_90
+ divPos = s + (l * OFFSCREEN_ASYMMETRIC_RATIO);
+ createAppLayout(SNAP_TO_2_10_90, divPos);
+
+ // SNAP_TO_2_33_66
+ divPos = s + (l * ONSCREEN_ONLY_ASYMMETRIC_RATIO);
+ createAppLayout(SNAP_TO_2_33_66, divPos);
+
+ // SNAP_TO_2_50_50
+ divPos = s + (l * MIDDLE_RATIO);
+ createAppLayout(SNAP_TO_2_50_50, divPos);
+
+ // SNAP_TO_2_66_33
+ divPos = s + (l * (1 - ONSCREEN_ONLY_ASYMMETRIC_RATIO));
+ createAppLayout(SNAP_TO_2_66_33, divPos);
+
+ // SNAP_TO_2_90_10
+ divPos = s + (l * (1 - OFFSCREEN_ASYMMETRIC_RATIO));
+ createAppLayout(SNAP_TO_2_90_10, divPos);
+
+ // SNAP_TO_3_10_45_45
+ divPos = s + (l * OFFSCREEN_ASYMMETRIC_RATIO);
+ divPos2 = e - ((l * (1 - OFFSCREEN_ASYMMETRIC_RATIO)) / 2f);
+ createAppLayout(SNAP_TO_3_10_45_45, divPos, divPos2);
+
+ // SNAP_TO_3_33_33_33
+ divPos = s + (l * ONSCREEN_ONLY_ASYMMETRIC_RATIO);
+ divPos2 = e - (l * ONSCREEN_ONLY_ASYMMETRIC_RATIO);
+ createAppLayout(SNAP_TO_3_33_33_33, divPos, divPos2);
+
+ // SNAP_TO_3_45_45_10
+ divPos = s + ((l * (1 - OFFSCREEN_ASYMMETRIC_RATIO)) / 2f);
+ divPos2 = e - (l * OFFSCREEN_ASYMMETRIC_RATIO);
+ createAppLayout(SNAP_TO_3_45_45_10, divPos, divPos2);
+
+ if (DEBUG) {
+ dump();
+ }
+ }
+
+ /**
+ * Creates a two-app layout and enters it into the layout map.
+ * @param divPos The position of the divider.
+ */
+ private void createAppLayout(@SplitScreenState int state, float divPos) {
+ List<RectF> list = new ArrayList<>();
+ RectF rect1 = new RectF(mUsableArea);
+ RectF rect2 = new RectF(mUsableArea);
+ if (mIsLeftRightSplit) {
+ rect1.right = divPos - mHalfDiv;
+ rect2.left = divPos + mHalfDiv;
+ } else {
+ rect1.top = divPos - mHalfDiv;
+ rect2.bottom = divPos + mHalfDiv;
+ }
+ list.add(rect1);
+ list.add(rect2);
+ mLayouts.put(state, list);
+ }
+
+ /**
+ * Creates a three-app layout and enters it into the layout map.
+ * @param divPos1 The position of the first divider.
+ * @param divPos2 The position of the second divider.
+ */
+ private void createAppLayout(@SplitScreenState int state, float divPos1, float divPos2) {
+ List<RectF> list = new ArrayList<>();
+ RectF rect1 = new RectF(mUsableArea);
+ RectF rect2 = new RectF(mUsableArea);
+ RectF rect3 = new RectF(mUsableArea);
+ if (mIsLeftRightSplit) {
+ rect1.right = divPos1 - mHalfDiv;
+ rect2.left = divPos1 + mHalfDiv;
+ rect2.right = divPos2 - mHalfDiv;
+ rect3.left = divPos2 + mHalfDiv;
+ } else {
+ rect1.right = divPos1 - mHalfDiv;
+ rect2.left = divPos1 + mHalfDiv;
+ rect3.right = divPos2 - mHalfDiv;
+ rect3.left = divPos2 + mHalfDiv;
+ }
+ list.add(rect1);
+ list.add(rect2);
+ list.add(rect3);
+ mLayouts.put(state, list);
+ }
+
+ /** Logs all calculated layouts */
+ private void dump() {
+ mLayouts.forEach((k, v) -> {
+ Log.d(TAG, stateToString(k));
+ v.forEach(rect -> Log.d(TAG, " - " + rect.toShortString()));
+ });
+ }
+
+ /** Returns the layout associated with a given split state. */
+ List<RectF> getSpec(@SplitScreenState int state) {
+ return mLayouts.get(state);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
index 71758e0d2159..d1d133d16ae4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
@@ -19,11 +19,17 @@ package com.android.wm.shell.common.split;
import static com.android.wm.shell.shared.split.SplitScreenConstants.NOT_IN_SPLIT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SplitScreenState;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import java.util.List;
+
/**
* A class that manages the "state" of split screen. See {@link SplitScreenState} for definitions.
*/
public class SplitState {
private @SplitScreenState int mState = NOT_IN_SPLIT;
+ private SplitSpec mSplitSpec;
/** Updates the current state of split screen on this device. */
public void set(@SplitScreenState int newState) {
@@ -39,4 +45,16 @@ public class SplitState {
public void exit() {
set(NOT_IN_SPLIT);
}
+
+ /** Refresh the valid layouts for this display/orientation. */
+ public void populateLayouts(Rect displayBounds, int dividerSize, boolean isLeftRightSplit,
+ Rect pinnedTaskbarInsets) {
+ mSplitSpec =
+ new SplitSpec(displayBounds, dividerSize, isLeftRightSplit, pinnedTaskbarInsets);
+ }
+
+ /** Returns the layout associated with a given split state. */
+ public List<RectF> getLayout(@SplitScreenState int state) {
+ return mSplitSpec.getSpec(state);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index 62d5098f2a27..bc56637b2a1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -30,7 +30,7 @@ import com.android.internal.R
* desktop windowing environment.
*/
fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
- (isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1))
+ (isSystemUiTask(context, task) || (task.numActivities > 0 && task.isActivityStackTransparent))
&& !task.isTopActivityNoDisplay
private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
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 9d4b4bbb33de..fe6066c8c4fb 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
@@ -55,6 +55,7 @@ import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.api.CompatUIInfo;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -70,7 +71,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.IntPredicate;
import java.util.function.Predicate;
/**
@@ -667,9 +667,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ boolean overridesShowAppHandle = DesktopModeStatus.overridesShowAppHandle(mContext);
if (mUserAspectRatioSettingsLayout != null) {
if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)
- || mIsInDesktopMode) {
+ || mIsInDesktopMode || overridesShowAppHandle) {
mUserAspectRatioSettingsLayout.release();
mUserAspectRatioSettingsLayout = null;
} else {
@@ -682,8 +683,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
- if (mIsInDesktopMode) {
- // Return if in desktop mode.
+ if (mIsInDesktopMode || overridesShowAppHandle) {
+ // Return if in desktop mode or app handle menu is already showing change aspect ratio
+ // option.
return;
}
// Create a new UI layout.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
index 819b11095a9b..2d0a3f54f85f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
@@ -67,8 +67,8 @@ class LetterboxCommandHandler @Inject constructor(
return false
}
return when (args.size) {
- 1 -> onShellDisplayCommand(args[0], pw)
- 2 -> onShellUpdateCommand(args[0], args[1], pw)
+ 1 -> onNoParamsCommand(args[0], pw)
+ 2 -> onSingleParamCommand(args[0], args[1], pw)
else -> {
pw.println("Invalid command: " + args[0])
return false
@@ -89,11 +89,17 @@ class LetterboxCommandHandler @Inject constructor(
$prefix name, for example, @android:color/system_accent2_50.
$prefix backgroundColorReset"
$prefix Resets the background color to the default value."
+ $prefix cornerRadius"
+ $prefix Corners radius (in pixels) for activities in the letterbox mode."
+ $prefix If cornerRadius < 0, it will be ignored and corners of the"
+ $prefix activity won't be rounded."
+ $prefix cornerRadiusReset"
+ $prefix Resets the rounded corners radius to the default value."
""".trimIndent()
)
}
- private fun onShellUpdateCommand(command: String, value: String, pw: PrintWriter): Boolean {
+ private fun onSingleParamCommand(command: String, value: String, pw: PrintWriter): Boolean {
when (command) {
"backgroundColor" -> {
return invokeWhenValid(
@@ -120,10 +126,17 @@ class LetterboxCommandHandler @Inject constructor(
}
)
- "backgroundColorReset" -> {
- letterboxConfiguration.resetLetterboxBackgroundColor()
- return true
- }
+ "cornerRadius" -> return invokeWhenValid(
+ pw,
+ value,
+ ::strToInt{ it >= 0 },
+ { radius ->
+ letterboxConfiguration.setLetterboxActivityCornersRadius(radius)
+ },
+ { r ->
+ "$r is not a valid radius. It must be an integer >= 0."
+ }
+ )
else -> {
pw.println("Invalid command: $value")
@@ -132,7 +145,7 @@ class LetterboxCommandHandler @Inject constructor(
}
}
- private fun onShellDisplayCommand(command: String, pw: PrintWriter): Boolean {
+ private fun onNoParamsCommand(command: String, pw: PrintWriter): Boolean {
when (command) {
"backgroundColor" -> {
pw.println(
@@ -144,6 +157,24 @@ class LetterboxCommandHandler @Inject constructor(
return true
}
+ "backgroundColorReset" -> {
+ letterboxConfiguration.resetLetterboxBackgroundColor()
+ return true
+ }
+
+ "cornerRadius" -> {
+ pw.println(
+ " Rounded corners radius: " +
+ "${letterboxConfiguration.getLetterboxActivityCornersRadius()} px."
+ )
+ return true
+ }
+
+ "cornerRadiusReset" -> {
+ letterboxConfiguration.resetLetterboxActivityCornersRadius()
+ return true
+ }
+
else -> {
pw.println("Invalid command: $command")
return false
@@ -181,4 +212,15 @@ class LetterboxCommandHandler @Inject constructor(
} catch (e: IllegalArgumentException) {
null
}
+
+ // Converts a String to Int which if possible or it returns null otherwise.
+ // If a predicate is set, it also returns [null] if the predicate evaluate to [false].
+ private fun strToInt(predicate: (Int) -> Boolean = { _ -> true }): (String) -> Int? = { str ->
+ try {
+ val value = str.toInt()
+ if (predicate(value)) value else null
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
index 83a8e3103e3f..244ff99cadd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
@@ -36,6 +36,21 @@ class LetterboxConfiguration @Inject constructor(
// Color resource id for the solid color letterbox background type.
private var letterboxBackgroundColorResourceIdOverride: Int? = null
+ // Default value for corners radius for activities presented in the letterbox mode.
+ // Values < 0 will be ignored.
+ private val letterboxActivityDefaultCornersRadius: Int
+
+ // Current corners radius for activities presented in the letterbox mode.
+ // Values can be modified at runtime and values < 0 will be ignored.
+ private var letterboxActivityCornersRadius = 0
+
+ init {
+ letterboxActivityDefaultCornersRadius = context.resources.getInteger(
+ R.integer.config_letterboxActivityCornersRadius
+ )
+ letterboxActivityCornersRadius = letterboxActivityDefaultCornersRadius
+ }
+
/**
* Sets color of letterbox background which is used when using the solid background mode.
*/
@@ -64,7 +79,7 @@ class LetterboxConfiguration @Inject constructor(
}
// Query color dynamically because material colors extracted from wallpaper are updated
// when wallpaper is changed.
- return Color.valueOf(context.getResources().getColor(colorId!!, /* theme */null))
+ return Color.valueOf(context.getResources().getColor(colorId!!, null))
}
/**
@@ -79,4 +94,33 @@ class LetterboxConfiguration @Inject constructor(
* The background color for the Letterbox.
*/
fun getBackgroundColorRgbArray(): FloatArray = getLetterboxBackgroundColor().components
+
+ /**
+ * Overrides corners radius for activities presented in the letterbox mode. Values < 0,
+ * will be ignored and corners of the activity won't be rounded.
+ */
+ fun setLetterboxActivityCornersRadius(cornersRadius: Int) {
+ letterboxActivityCornersRadius = cornersRadius
+ }
+
+ /**
+ * Resets corners radius for activities presented in the letterbox mode.
+ */
+ fun resetLetterboxActivityCornersRadius() {
+ letterboxActivityCornersRadius = letterboxActivityDefaultCornersRadius
+ }
+
+ /**
+ * Whether corners of letterboxed activities are rounded.
+ */
+ fun isLetterboxActivityCornersRounded(): Boolean {
+ return getLetterboxActivityCornersRadius() > 0
+ }
+
+ /**
+ * Gets corners radius for activities presented in the letterbox mode.
+ */
+ fun getLetterboxActivityCornersRadius(): Int {
+ return maxOf(letterboxActivityCornersRadius, 0)
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
index 9e3edf68ed03..0c3769e778cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.compatui.letterbox
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.MULTIPLE_SURFACES
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.SINGLE_SURFACE
import com.android.wm.shell.dagger.WMSingleton
import javax.inject.Inject
@@ -24,24 +26,28 @@ import javax.inject.Inject
* implementing letterbox in shell.
*/
@WMSingleton
-class LetterboxControllerStrategy @Inject constructor() {
+class LetterboxControllerStrategy @Inject constructor(
+ private val letterboxConfiguration: LetterboxConfiguration
+) {
// Different letterbox implementation modes.
enum class LetterboxMode { SINGLE_SURFACE, MULTIPLE_SURFACES }
@Volatile
- private var currentMode: LetterboxMode = LetterboxMode.SINGLE_SURFACE
+ private var currentMode: LetterboxMode = SINGLE_SURFACE
fun configureLetterboxMode() {
// TODO(b/377875146): Define criteria for switching between [LetterboxMode]s.
- currentMode = if (android.os.SystemProperties.getInt(
- "multi_interface",
- 0
- ) == 0
- ) {
- LetterboxMode.SINGLE_SURFACE
+ // At the moment, we use the presence of rounded corners to understand if to use a single
+ // surface or multiple surfaces for the letterbox areas. This rule will change when
+ // considering transparent activities which won't have rounded corners leading to the
+ // [MULTIPLE_SURFACES] option.
+ // The chosen strategy will depend on performance considerations,
+ // including surface memory usage and the impact of the rounded corners solution.
+ currentMode = if (letterboxConfiguration.isLetterboxActivityCornersRounded()) {
+ SINGLE_SURFACE
} else {
- LetterboxMode.MULTIPLE_SURFACES
+ MULTIPLE_SURFACES
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
index ef964f40dab3..8e149ac2869a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
@@ -66,3 +66,41 @@ infix fun LetterboxController.append(other: LetterboxController) = object : Lett
other.dump()
}
}
+
+object LetterboxUtils {
+ // Utility methods about Maps usage in Letterbox.
+ object Maps {
+ /*
+ * Executes [onFound] on the [item] for a given [key] if present or
+ * [onMissed] if the [key] is not present.
+ */
+ fun <V, K> MutableMap<K, V>.runOnItem(
+ key: K,
+ onFound: (V) -> Unit = { _ -> },
+ onMissed: (
+ K,
+ MutableMap<K, V>
+ ) -> Unit = { _, _ -> }
+ ) {
+ this[key]?.let {
+ return onFound(it)
+ }
+ return onMissed(key, this)
+ }
+ }
+
+ // Utility methods about Transaction usage in Letterbox.
+ object Transactions {
+ // Sets position and crops in one method.
+ fun Transaction.moveAndCrop(
+ surface: SurfaceControl,
+ rect: Rect
+ ): Transaction =
+ setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(
+ surface,
+ rect.width(),
+ rect.height()
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
index 5129d03b9dbc..6861aa3763b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
@@ -20,6 +20,8 @@ import android.graphics.Rect
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
import javax.inject.Inject
@@ -101,23 +103,6 @@ class MultiSurfaceLetterboxController @Inject constructor(
ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
}
- /*
- * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present.
- */
- private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem(
- key: LetterboxKey,
- onFound: (LetterboxSurfaces) -> Unit = { _ -> },
- onMissed: (
- LetterboxKey,
- MutableMap<LetterboxKey, LetterboxSurfaces>
- ) -> Unit = { _, _ -> }
- ) {
- this[key]?.let {
- return onFound(it)
- }
- return onMissed(key, this)
- }
-
private fun SurfaceControl?.remove(
tx: Transaction
) = this?.let {
@@ -131,17 +116,6 @@ class MultiSurfaceLetterboxController @Inject constructor(
tx.setVisibility(this, visible)
}
- private fun Transaction.moveAndCrop(
- surface: SurfaceControl,
- rect: Rect
- ): Transaction =
- setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
- .setWindowCrop(
- surface,
- rect.width(),
- rect.height()
- )
-
private fun LetterboxSurfaces.updateSurfacesBounds(
tx: Transaction,
taskBounds: Rect,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
index a67f6082c892..8e1cdee0c862 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
@@ -20,6 +20,8 @@ import android.graphics.Rect
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
import javax.inject.Inject
@@ -106,32 +108,4 @@ class SingleSurfaceLetterboxController @Inject constructor(
override fun dump() {
ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
}
-
- /*
- * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present.
- */
- private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem(
- key: LetterboxKey,
- onFound: (SurfaceControl) -> Unit = { _ -> },
- onMissed: (
- LetterboxKey,
- MutableMap<LetterboxKey, SurfaceControl>
- ) -> Unit = { _, _ -> }
- ) {
- this[key]?.let {
- return onFound(it)
- }
- return onMissed(key, this)
- }
-
- private fun Transaction.moveAndCrop(
- surface: SurfaceControl,
- rect: Rect
- ): Transaction =
- setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
- .setWindowCrop(
- surface,
- rect.width(),
- rect.height()
- )
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt
new file mode 100644
index 000000000000..d5e0240ea9ad
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.dagger
+
+/**
+ * An interface implemented by the application that uses [WMComponent].
+ *
+ * This exposes the component to allow classes to do member injection for bindings where constructor
+ * injection is not possible, e.g. views.
+ */
+interface HasWMComponent {
+ fun getWMComponent(): WMComponent
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java
new file mode 100644
index 000000000000..76279ec8cfec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.dagger;
+
+import android.annotation.NonNull;
+
+import com.android.wm.shell.common.transition.TransitionStateHolder;
+import com.android.wm.shell.compatui.letterbox.LetterboxController;
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy;
+import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
+import com.android.wm.shell.compatui.letterbox.MixedLetterboxController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides Letterbox Shell implementation components to Dagger dependency Graph.
+ */
+@Module
+public abstract class LetterboxModule {
+
+ @WMSingleton
+ @Provides
+ static LetterboxTransitionObserver provideLetterboxTransitionObserver(
+ @NonNull ShellInit shellInit,
+ @NonNull Transitions transitions,
+ @NonNull LetterboxController letterboxController,
+ @NonNull TransitionStateHolder transitionStateHolder,
+ @NonNull LetterboxControllerStrategy letterboxControllerStrategy
+ ) {
+ return new LetterboxTransitionObserver(shellInit, transitions, letterboxController,
+ transitionStateHolder, letterboxControllerStrategy);
+ }
+
+ @WMSingleton
+ @Binds
+ abstract LetterboxController bindsLetterboxController(
+ MixedLetterboxController letterboxController);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index aebd94fc173a..5b6b897e55d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.dagger.pip.TvPipModule;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
@@ -93,11 +94,12 @@ public class TvWMShellModule {
SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
SystemWindows systemWindows) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
- splitState, mainExecutor, mainHandler, systemWindows);
+ splitState, mainExecutor, mainHandler, bgExecutor, systemWindows);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
index a3cdb2eff601..c493aadd57b0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.wm.shell.dagger;
import android.os.HandlerThread;
import androidx.annotation.Nullable;
-import com.android.systemui.SystemUIInitializer;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.dagger.WMShellModule;
-import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.keyguard.KeyguardTransitions;
@@ -45,12 +42,11 @@ import java.util.Optional;
/**
* Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
- * from the WM component into the SysUI component (in
- * {@link SystemUIInitializer#init(boolean)}), and references the specific dependencies
+ * from the WM component into the SysUI component, and references the specific dependencies
* provided by its particular device/form-factor SystemUI implementation.
*
- * ie. {@link WMComponent} includes {@link WMShellModule}
- * and {@code TvWMComponent} includes {@link com.android.wm.shell.dagger.TvWMShellModule}
+ * <p> ie. {@link WMComponent} includes {@link WMShellModule} and {@code TvWMComponent} includes
+ * {@link TvWMShellModule}
*/
@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
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 de86b22e0a99..6c805c87c08f 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
@@ -64,6 +64,7 @@ import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -531,6 +532,13 @@ public abstract class WMShellBaseModule {
pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
}
+ @WMSingleton
+ @Provides
+ static PipAppOpsListener providePipAppOpsListener(Context context,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipAppOpsListener(context, mainExecutor);
+ }
+
//
// Bubbles (optional feature)
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index c5644a8f6876..d7ddbdeaa6da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.dagger;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import android.content.Context;
@@ -205,13 +206,14 @@ public abstract class WMShellConcurrencyModule {
}
/**
- * Provides a Shell background thread Executor for low priority background tasks.
+ * Provides a Shell background thread Executor for low priority background tasks. The thread
+ * may also be boosted to THREAD_PRIORITY_FOREGROUND if necessary.
*/
@WMSingleton
@Provides
@ShellBackgroundThread
public static ShellExecutor provideSharedBackgroundExecutor(
@ShellBackgroundThread Handler handler) {
- return new HandlerExecutor(handler);
+ return new HandlerExecutor(handler, THREAD_PRIORITY_BACKGROUND, THREAD_PRIORITY_FOREGROUND);
}
}
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 806be8c75610..ace7f078bb10 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
@@ -71,12 +71,8 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.split.SplitState;
-import com.android.wm.shell.common.transition.TransitionStateHolder;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
-import com.android.wm.shell.compatui.letterbox.LetterboxController;
-import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
-import com.android.wm.shell.compatui.letterbox.MixedLetterboxController;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
@@ -155,7 +151,10 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
@@ -182,7 +181,8 @@ import java.util.Optional;
* <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common
* dependencies should go into {@link WMShellBaseModule}.
*/
-@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class})
+@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class,
+ LetterboxModule.class})
public abstract class WMShellModule {
//
@@ -304,6 +304,7 @@ public abstract class WMShellModule {
Transitions transitions,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
FocusTransitionObserver focusTransitionObserver,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel) {
if (desktopModeWindowDecorViewModel.isPresent()) {
return desktopModeWindowDecorViewModel.get();
@@ -321,7 +322,8 @@ public abstract class WMShellModule {
rootTaskDisplayAreaOrganizer,
syncQueue,
transitions,
- focusTransitionObserver);
+ focusTransitionObserver,
+ windowDecorViewHostSupplier);
}
@WMSingleton
@@ -346,8 +348,16 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
- @ShellMainThread @NonNull CoroutineScope mainScope) {
+ static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier(
+ @NonNull Context context,
+ @ShellMainThread @NonNull CoroutineScope mainScope,
+ @NonNull ShellInit shellInit) {
+ final int poolSize = DesktopModeStatus.getWindowDecorScvhPoolSize(context);
+ final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize();
+ if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && poolSize > 0) {
+ return new PooledWindowDecorViewHostSupplier(
+ context, mainScope, shellInit, poolSize, preWarmSize);
+ }
return new DefaultWindowDecorViewHostSupplier(mainScope);
}
@@ -508,7 +518,8 @@ public abstract class WMShellModule {
MultiInstanceHelper multiInstanceHelper,
SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ @ShellMainThread Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor) {
return new SplitScreenController(
context,
shellInit,
@@ -532,7 +543,8 @@ public abstract class WMShellModule {
multiInstanceHelper,
splitState,
mainExecutor,
- mainHandler);
+ mainHandler,
+ bgExecutor);
}
//
@@ -758,6 +770,8 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static DesktopTilingDecorViewModel provideDesktopTilingViewModel(Context context,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
DisplayController displayController,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
@@ -766,9 +780,12 @@ public abstract class WMShellModule {
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
ReturnToDragStartAnimator returnToDragStartAnimator,
@DynamicOverride DesktopUserRepositories desktopUserRepositories,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ WindowDecorTaskResourceLoader windowDecorTaskResourceLoader) {
return new DesktopTilingDecorViewModel(
context,
+ mainDispatcher,
+ bgScope,
displayController,
rootTaskDisplayAreaOrganizer,
syncQueue,
@@ -777,7 +794,8 @@ public abstract class WMShellModule {
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
desktopUserRepositories,
- desktopModeEventLogger
+ desktopModeEventLogger,
+ windowDecorTaskResourceLoader
);
}
@@ -894,6 +912,8 @@ public abstract class WMShellModule {
@ShellMainThread ShellExecutor shellExecutor,
@ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -911,6 +931,7 @@ public abstract class WMShellModule {
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
AppHandleEducationController appHandleEducationController,
@@ -919,21 +940,34 @@ public abstract class WMShellModule {
Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
- DesktopModeUiEventLogger desktopModeUiEventLogger
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
+ WindowDecorTaskResourceLoader taskResourceLoader
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
}
return Optional.of(new DesktopModeWindowDecorViewModel(context, shellExecutor, mainHandler,
- mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager,
+ mainChoreographer, mainDispatcher, bgScope, bgExecutor,
+ shellInit, shellCommandHandler, windowManager,
taskOrganizer, desktopUserRepositories, displayController, shellController,
displayInsetsController, syncQueue, transitions, desktopTasksController,
desktopImmersiveController.get(),
rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser,
- assistContentRequester, multiInstanceHelper, desktopTasksLimiter,
- appHandleEducationController, appToWebEducationController,
+ assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper,
+ desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
- focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger));
+ focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
+ taskResourceLoader));
+ }
+
+ @WMSingleton
+ @Provides
+ static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader(
+ @NonNull Context context, @NonNull ShellInit shellInit,
+ @NonNull ShellController shellController,
+ @NonNull ShellCommandHandler shellCommandHandler) {
+ return new WindowDecorTaskResourceLoader(context, shellInit, shellController,
+ shellCommandHandler);
}
@WMSingleton
@@ -1015,12 +1049,14 @@ public abstract class WMShellModule {
static DesktopUserRepositories provideDesktopUserRepositories(
Context context,
ShellInit shellInit,
+ ShellController shellController,
DesktopPersistentRepository desktopPersistentRepository,
DesktopRepositoryInitializer desktopRepositoryInitializer,
@ShellMainThread CoroutineScope mainScope,
UserManager userManager
) {
- return new DesktopUserRepositories(context, shellInit, desktopPersistentRepository,
+ return new DesktopUserRepositories(context, shellInit, shellController,
+ desktopPersistentRepository,
desktopRepositoryInitializer,
mainScope, userManager);
}
@@ -1329,25 +1365,4 @@ public abstract class WMShellModule {
return new Object();
}
- //
- // App Compat
- //
-
- @WMSingleton
- @Provides
- static LetterboxTransitionObserver provideLetterboxTransitionObserver(
- @NonNull ShellInit shellInit,
- @NonNull Transitions transitions,
- @NonNull LetterboxController letterboxController,
- @NonNull TransitionStateHolder transitionStateHolder,
- @NonNull LetterboxControllerStrategy letterboxControllerStrategy
- ) {
- return new LetterboxTransitionObserver(shellInit, transitions, letterboxController,
- transitionStateHolder, letterboxControllerStrategy);
- }
-
- @WMSingleton
- @Binds
- abstract LetterboxController bindsLetterboxController(
- MixedLetterboxController letterboxController);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index cfdfe3d52011..64b6c0f8e75d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -203,14 +203,6 @@ public abstract class Pip1Module {
@WMSingleton
@Provides
- static PipAppOpsListener providePipAppOpsListener(Context context,
- PipTouchHandler pipTouchHandler,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
- }
-
- @WMSingleton
- @Provides
static PipMotionHelper providePipMotionHelper(Context context,
@ShellMainThread ShellExecutor mainExecutor,
PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 3a9961917f79..94a6e5862b6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -28,6 +28,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -113,6 +114,8 @@ public abstract class Pip2Module {
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
PipTouchHandler pipTouchHandler,
+ PipAppOpsListener pipAppOpsListener,
+ PhonePipMenuController pipMenuController,
@ShellMainThread ShellExecutor mainExecutor) {
if (!PipUtils.isPip2ExperimentEnabled()) {
return Optional.empty();
@@ -121,7 +124,8 @@ public abstract class Pip2Module {
context, shellInit, shellCommandHandler, shellController, displayController,
displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
- pipTransitionState, pipTouchHandler, mainExecutor));
+ pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
+ mainExecutor));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 78e676f8cd45..b9f548297189 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -233,12 +233,4 @@ public abstract class TvPipModule {
static PipParamsChangedForwarder providePipParamsChangedForwarder() {
return new PipParamsChangedForwarder();
}
-
- @WMSingleton
- @Provides
- static PipAppOpsListener providePipAppOpsListener(Context context,
- PipTaskOrganizer pipTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new PipAppOpsListener(context, pipTaskOrganizer::removePip, mainExecutor);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index 8e2a412764eb..536dc2a58534 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -74,13 +74,11 @@ class DesktopImmersiveController(
{ SurfaceControl.Transaction() },
)
- @VisibleForTesting var state: TransitionState? = null
-
- @VisibleForTesting val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>()
+ @VisibleForTesting val pendingImmersiveTransitions = mutableListOf<PendingTransition>()
/** Whether there is an immersive transition that hasn't completed yet. */
private val inProgress: Boolean
- get() = state != null || pendingExternalExitTransitions.isNotEmpty()
+ get() = pendingImmersiveTransitions.isNotEmpty()
private val rectEvaluator = RectEvaluator()
@@ -101,20 +99,19 @@ class DesktopImmersiveController(
if (inProgress) {
logV(
"Cannot start entry because transition(s) already in progress: %s",
- getRunningTransitions(),
+ pendingImmersiveTransitions,
)
return
}
val wct = WindowContainerTransaction().apply { setBounds(taskInfo.token, Rect()) }
logV("Moving task ${taskInfo.taskId} into immersive mode")
val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
- state =
- TransitionState(
- transition = transition,
- displayId = taskInfo.displayId,
- taskId = taskInfo.taskId,
- direction = Direction.ENTER,
- )
+ addPendingImmersiveTransition(
+ taskId = taskInfo.taskId,
+ displayId = taskInfo.displayId,
+ direction = Direction.ENTER,
+ transition = transition,
+ )
}
/** Starts a transition to move an immersive task out of immersive. */
@@ -123,7 +120,7 @@ class DesktopImmersiveController(
if (inProgress) {
logV(
"Cannot start exit because transition(s) already in progress: %s",
- getRunningTransitions(),
+ pendingImmersiveTransitions,
)
return
}
@@ -134,13 +131,12 @@ class DesktopImmersiveController(
}
logV("Moving task %d out of immersive mode, reason: %s", taskInfo.taskId, reason)
val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
- state =
- TransitionState(
- transition = transition,
- displayId = taskInfo.displayId,
- taskId = taskInfo.taskId,
- direction = Direction.EXIT,
- )
+ addPendingImmersiveTransition(
+ taskId = taskInfo.taskId,
+ displayId = taskInfo.displayId,
+ direction = Direction.EXIT,
+ transition = transition,
+ )
}
/**
@@ -194,7 +190,13 @@ class DesktopImmersiveController(
return ExitResult.Exit(
exitingTask = immersiveTask,
runOnTransitionStart = { transition ->
- addPendingImmersiveExit(immersiveTask, displayId, transition)
+ addPendingImmersiveTransition(
+ taskId = immersiveTask,
+ displayId = displayId,
+ direction = Direction.EXIT,
+ transition = transition,
+ animate = false,
+ )
},
)
}
@@ -220,10 +222,12 @@ class DesktopImmersiveController(
return ExitResult.Exit(
exitingTask = taskInfo.taskId,
runOnTransitionStart = { transition ->
- addPendingImmersiveExit(
+ addPendingImmersiveTransition(
taskId = taskInfo.taskId,
displayId = taskInfo.displayId,
+ direction = Direction.EXIT,
transition = transition,
+ animate = false,
)
},
)
@@ -233,14 +237,26 @@ class DesktopImmersiveController(
/** Whether the [change] in the [transition] is a known immersive change. */
fun isImmersiveChange(transition: IBinder, change: TransitionInfo.Change): Boolean {
- return pendingExternalExitTransitions.any {
+ return pendingImmersiveTransitions.any {
it.transition == transition && it.taskId == change.taskInfo?.taskId
}
}
- private fun addPendingImmersiveExit(taskId: Int, displayId: Int, transition: IBinder) {
- pendingExternalExitTransitions.add(
- ExternalPendingExit(taskId = taskId, displayId = displayId, transition = transition)
+ private fun addPendingImmersiveTransition(
+ taskId: Int,
+ displayId: Int,
+ direction: Direction,
+ transition: IBinder,
+ animate: Boolean = true,
+ ) {
+ pendingImmersiveTransitions.add(
+ PendingTransition(
+ taskId = taskId,
+ displayId = displayId,
+ direction = direction,
+ transition = transition,
+ animate = animate,
+ )
)
}
@@ -251,19 +267,17 @@ class DesktopImmersiveController(
finishTransaction: SurfaceControl.Transaction,
finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
- val state = requireState()
- check(state.transition == transition) {
- "Transition $transition did not match expected state=$state"
- }
+ val immersiveTransition = getImmersiveTransition(transition) ?: return false
+ if (!immersiveTransition.animate) return false
logD("startAnimation transition=%s", transition)
animateResize(
- targetTaskId = state.taskId,
+ targetTaskId = immersiveTransition.taskId,
info = info,
startTransaction = startTransaction,
finishTransaction = finishTransaction,
finishCallback = {
finishCallback.onTransitionFinished(/* wct= */ null)
- clearState()
+ pendingImmersiveTransitions.remove(immersiveTransition)
},
)
return true
@@ -346,18 +360,6 @@ class DesktopImmersiveController(
request: TransitionRequestInfo,
): WindowContainerTransaction? = null
- override fun onTransitionConsumed(
- transition: IBinder,
- aborted: Boolean,
- finishTransaction: SurfaceControl.Transaction?,
- ) {
- val state = this.state ?: return
- if (transition == state.transition && aborted) {
- clearState()
- }
- super.onTransitionConsumed(transition, aborted, finishTransaction)
- }
-
/**
* Called when any transition in the system is ready to play. This is needed to update the
* repository state before window decorations are drawn (which happens immediately after
@@ -371,67 +373,42 @@ class DesktopImmersiveController(
finishTransaction: SurfaceControl.Transaction,
) {
val desktopRepository: DesktopRepository = desktopUserRepositories.current
- // Check if this is a pending external exit transition.
- val pendingExit =
- pendingExternalExitTransitions.firstOrNull { pendingExit ->
- pendingExit.transition == transition
- }
- if (pendingExit != null) {
- if (info.hasTaskChange(taskId = pendingExit.taskId)) {
- if (desktopRepository.isTaskInFullImmersiveState(pendingExit.taskId)) {
- logV("Pending external exit for task#%d verified", pendingExit.taskId)
- desktopRepository.setTaskInFullImmersiveState(
- displayId = pendingExit.displayId,
- taskId = pendingExit.taskId,
- immersive = false,
- )
- if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
- desktopRepository.removeBoundsBeforeFullImmersive(pendingExit.taskId)
- }
- }
- }
- return
- }
+ val pendingTransition = getImmersiveTransition(transition)
- // Check if this is a direct immersive enter/exit transition.
- if (transition == state?.transition) {
- val state = requireState()
- val immersiveChange =
- info.changes.firstOrNull { c -> c.taskInfo?.taskId == state.taskId }
+ if (pendingTransition != null) {
+ val taskId = pendingTransition.taskId
+ val immersiveChange = info.getTaskChange(taskId = taskId)
if (immersiveChange == null) {
logV(
- "Direct move for task#%d in %s direction missing immersive change.",
- state.taskId,
- state.direction,
+ "Transition for task#%d in %s direction missing immersive change.",
+ taskId,
+ pendingTransition.direction,
)
return
}
- val startBounds = immersiveChange.startAbsBounds
- logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction)
-
- when (state.direction) {
- Direction.ENTER -> {
- desktopRepository.setTaskInFullImmersiveState(
- displayId = state.displayId,
- taskId = state.taskId,
- immersive = true,
- )
- if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
- desktopRepository.saveBoundsBeforeFullImmersive(state.taskId, startBounds)
+ logV(
+ "Immersive transition for task#%d in %s direction verified",
+ taskId,
+ pendingTransition.direction,
+ )
+ desktopRepository.setTaskInFullImmersiveState(
+ displayId = pendingTransition.displayId,
+ taskId = taskId,
+ immersive = pendingTransition.direction == Direction.ENTER,
+ )
+ if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
+ when (pendingTransition.direction) {
+ Direction.EXIT -> {
+ desktopRepository.removeBoundsBeforeFullImmersive(taskId)
}
- }
- Direction.EXIT -> {
- desktopRepository.setTaskInFullImmersiveState(
- displayId = state.displayId,
- taskId = state.taskId,
- immersive = false,
- )
- if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
- desktopRepository.removeBoundsBeforeFullImmersive(state.taskId)
+ Direction.ENTER -> {
+ desktopRepository.saveBoundsBeforeFullImmersive(
+ taskId,
+ immersiveChange.startAbsBounds,
+ )
}
}
}
- return
}
// Check if this is an untracked exit transition, like display rotation.
@@ -450,35 +427,31 @@ class DesktopImmersiveController(
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- val pendingExit =
- pendingExternalExitTransitions.firstOrNull { pendingExit ->
- pendingExit.transition == merged
+ val pendingTransition =
+ pendingImmersiveTransitions.firstOrNull { pendingTransition ->
+ pendingTransition.transition == merged
}
- if (pendingExit != null) {
+ if (pendingTransition != null) {
logV(
- "Pending exit transition %s for task#%s merged into %s",
+ "Pending transition %s for task#%s merged into %s",
merged,
- pendingExit.taskId,
+ pendingTransition.taskId,
playing,
)
- pendingExit.transition = playing
+ pendingTransition.transition = playing
}
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- val pendingExit =
- pendingExternalExitTransitions.firstOrNull { pendingExit ->
- pendingExit.transition == transition
- }
- if (pendingExit != null) {
- logV("Pending exit transition %s for task#%s finished", transition, pendingExit)
- pendingExternalExitTransitions.remove(pendingExit)
+ val pendingTransition = getImmersiveTransition(transition)
+ if (pendingTransition != null) {
+ logV("Pending exit transition %s for task#%s finished", transition, pendingTransition)
+ pendingImmersiveTransitions.remove(pendingTransition)
}
}
- private fun clearState() {
- state = null
- }
+ private fun getImmersiveTransition(transition: IBinder) =
+ pendingImmersiveTransitions.firstOrNull { it.transition == transition }
private fun getExitDestinationBounds(taskInfo: RunningTaskInfo): Rect {
val displayLayout =
@@ -496,24 +469,13 @@ class DesktopImmersiveController(
}
}
- private fun requireState(): TransitionState =
- state ?: error("Expected non-null transition state")
-
- private fun getRunningTransitions(): List<IBinder> {
- val running = mutableListOf<IBinder>()
- state?.let { running.add(it.transition) }
- pendingExternalExitTransitions.forEach { running.add(it.transition) }
- return running
- }
-
- private fun TransitionInfo.hasTaskChange(taskId: Int): Boolean =
- changes.any { c -> c.taskInfo?.taskId == taskId }
+ private fun TransitionInfo.getTaskChange(taskId: Int): TransitionInfo.Change? =
+ changes.firstOrNull { c -> c.taskInfo?.taskId == taskId }
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopImmersiveController")
- pw.println(innerPrefix + "state=" + state)
- pw.println(innerPrefix + "pendingExternalExitTransitions=" + pendingExternalExitTransitions)
+ pw.println(innerPrefix + "pendingImmersiveTransitions=" + pendingImmersiveTransitions)
}
/** The state of the currently running transition. */
@@ -526,12 +488,22 @@ class DesktopImmersiveController(
)
/**
- * Tracks state of a transition involving an immersive exit that is external to this class' own
- * transitions. This usually means transitions that exit immersive mode as a side-effect and not
- * the primary action (for example, minimizing the immersive task or launching a new task on top
- * of the immersive task).
+ * Tracks state of a transition involving an immersive enter or exit. This includes both
+ * transitions that should and should not be animated by this handler.
+ *
+ * @param taskId of the task that should enter/exit immersive mode
+ * @param displayId of the display that should enter/exit immersive mode
+ * @param direction of the immersive transition
+ * @param transition that will apply this transaction
+ * @param animate whether transition should be animated by this handler
*/
- data class ExternalPendingExit(val taskId: Int, val displayId: Int, var transition: IBinder)
+ data class PendingTransition(
+ val taskId: Int,
+ val displayId: Int,
+ val direction: Direction,
+ var transition: IBinder,
+ val animate: Boolean,
+ )
/** The result of an external exit request. */
sealed class ExitResult {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 7764688695f7..d404634b0db0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -77,6 +77,10 @@ class DesktopMixedTransitionHandler(
override fun startMinimizedModeTransition(wct: WindowContainerTransaction?): IBinder =
freeformTaskTransitionHandler.startMinimizedModeTransition(wct)
+ /** Delegates starting PiP transition to [FreeformTaskTransitionHandler]. */
+ override fun startPipTransition(wct: WindowContainerTransaction?): IBinder =
+ freeformTaskTransitionHandler.startPipTransition(wct)
+
/** Starts close transition and handles or delegates desktop task close animation. */
override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder {
if (
@@ -235,7 +239,6 @@ class DesktopMixedTransitionHandler(
pending.minimizingTask?.let { minimizingTask -> findTaskChange(info, minimizingTask) }
val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
if (launchChange == null) {
- check(minimizeChange == null)
check(immersiveExitChange == null)
logV("No launch Change, returning")
return false
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 14623cf9e703..606a729305b4 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
@@ -235,8 +235,7 @@ fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
/** Returns true if task bound is equal to stable bounds else returns false. */
fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
- return taskBounds.width() == stableBounds.width() &&
- taskBounds.height() == stableBounds.height()
+ return taskBounds == stableBounds
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 80d8ecc127fe..cd37113666bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -53,6 +53,7 @@ import android.view.animation.DecelerateInterpolator;
import androidx.annotation.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -245,9 +246,17 @@ public class DesktopModeVisualIndicator {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mContext.getResources();
final DisplayMetrics metrics = resources.getDisplayMetrics();
- final int screenWidth = metrics.widthPixels;
- final int screenHeight = metrics.heightPixels;
-
+ final int screenWidth;
+ final int screenHeight;
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ final DisplayLayout displayLayout =
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ screenWidth = displayLayout.width();
+ screenHeight = displayLayout.height();
+ } else {
+ screenWidth = metrics.widthPixels;
+ screenHeight = metrics.heightPixels;
+ }
mView = new View(mContext);
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
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 bccb609c41e8..c4abee3bed78 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
@@ -220,11 +220,18 @@ class DesktopRepository(
fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
/** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
- fun isOnlyVisibleNonClosingTask(taskId: Int): Boolean =
- desktopTaskDataSequence().any {
+ fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
+ val seq =
+ if (displayId != INVALID_DISPLAY) {
+ sequenceOf(desktopTaskDataByDisplayId[displayId]).filterNotNull()
+ } else {
+ desktopTaskDataSequence()
+ }
+ return seq.any {
it.visibleTasks.subtract(it.closingTasks).subtract(it.minimizedTasks).singleOrNull() ==
taskId
}
+ }
fun getActiveTasks(displayId: Int): ArraySet<Int> =
ArraySet(desktopTaskDataByDisplayId[displayId]?.activeTasks)
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 7bb7242c587c..609ac0aac381 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
@@ -50,6 +50,7 @@ import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.widget.Toast
import android.window.DesktopModeFlags
@@ -67,7 +68,6 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.policy.ScreenDecorationsUtils
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.Flags.enableFlexibleSplit
@@ -221,6 +221,7 @@ class DesktopTasksController(
// Launch cookie used to identify a drag and drop transition to fullscreen after it has begun.
// Used to prevent handleRequest from moving the new fullscreen task to freeform.
private var dragAndDropFullscreenCookie: Binder? = null
+ private var pendingPipTransitionAndTask: Pair<IBinder, Int>? = null
init {
desktopMode = DesktopModeImpl()
@@ -362,8 +363,15 @@ class DesktopTasksController(
}
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
- requireNotNull(tdaInfo) {
- "This method can only be called with the ID of a display having non-null DisplayArea."
+ // A non-organized display (e.g., non-trusted virtual displays used in CTS) doesn't have
+ // TDA.
+ if (tdaInfo == null) {
+ logW(
+ "forceEnterDesktop cannot find DisplayAreaInfo for displayId=%d. This could happen" +
+ " when the display is a non-trusted virtual display.",
+ displayId,
+ )
+ return false
}
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
val isFreeformDisplay = tdaWindowingMode == WINDOWING_MODE_FREEFORM
@@ -375,12 +383,13 @@ class DesktopTasksController(
taskId: Int,
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
+ remoteTransition: RemoteTransition? = null,
): Boolean {
val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
if (runningTask == null) {
- return moveBackgroundTaskToDesktop(taskId, wct, transitionSource)
+ return moveBackgroundTaskToDesktop(taskId, wct, transitionSource, remoteTransition)
}
- moveRunningTaskToDesktop(runningTask, wct, transitionSource)
+ moveRunningTaskToDesktop(runningTask, wct, transitionSource, remoteTransition)
return true
}
@@ -388,6 +397,7 @@ class DesktopTasksController(
taskId: Int,
wct: WindowContainerTransaction,
transitionSource: DesktopModeTransitionSource,
+ remoteTransition: RemoteTransition? = null,
): Boolean {
if (recentTasksController?.findTaskInBackground(taskId) == null) {
logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
@@ -410,8 +420,17 @@ class DesktopTasksController(
.apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
.toBundle(),
)
- // TODO(343149901): Add DPI changes for task launch
- val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+
+ val transition: IBinder
+ if (remoteTransition != null) {
+ val transitionType = transitionType(remoteTransition)
+ val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
+ transition = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
+ remoteTransitionHandler.setTransition(transition)
+ } else {
+ // TODO(343149901): Add DPI changes for task launch
+ transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ }
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
@@ -425,6 +444,7 @@ class DesktopTasksController(
task: RunningTaskInfo,
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
+ remoteTransition: RemoteTransition? = null,
) {
if (
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
@@ -442,12 +462,21 @@ class DesktopTasksController(
excludeTaskId = task.taskId,
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
+
// Bring other apps to front first
val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
- val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ val transition: IBinder
+ if (remoteTransition != null) {
+ val transitionType = transitionType(remoteTransition)
+ val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
+ transition = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
+ remoteTransitionHandler.setTransition(transition)
+ } else {
+ transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ }
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
@@ -542,7 +571,7 @@ class DesktopTasksController(
): ((IBinder) -> Unit)? {
val taskId = taskInfo.taskId
desktopTilingDecorViewModel.removeTaskIfTiled(displayId, taskId)
- performDesktopExitCleanupIfNeeded(taskId, wct)
+ performDesktopExitCleanupIfNeeded(taskId, displayId, wct)
taskRepository.addClosingTask(displayId, taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(displayId, taskId)
@@ -558,10 +587,35 @@ class DesktopTasksController(
}
fun minimizeTask(taskInfo: RunningTaskInfo) {
+ val wct = WindowContainerTransaction()
+
+ val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false
+ // If task is going to PiP, start a PiP transition instead of a minimize transition
+ if (isMinimizingToPip) {
+ val requestInfo =
+ TransitionRequestInfo(
+ TRANSIT_PIP,
+ /* triggerTask= */ null,
+ taskInfo,
+ /* remoteTransition= */ null,
+ /* displayChange= */ null,
+ /* flags= */ 0,
+ )
+ val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
+ wct.merge(requestRes.second, true)
+ pendingPipTransitionAndTask =
+ freeformTaskTransitionStarter.startPipTransition(wct) to taskInfo.taskId
+ return
+ }
+
+ minimizeTaskInner(taskInfo)
+ }
+
+ private fun minimizeTaskInner(taskInfo: RunningTaskInfo) {
val taskId = taskInfo.taskId
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
- performDesktopExitCleanupIfNeeded(taskId, wct)
+ performDesktopExitCleanupIfNeeded(taskId, displayId, wct)
// Notify immersive handler as it might need to exit immersive state.
val exitResult =
desktopImmersiveController.exitImmersiveIfApplicable(
@@ -710,15 +764,12 @@ class DesktopTasksController(
displayId: Int = DEFAULT_DISPLAY,
): IBinder {
val taskIdToMinimize =
- if (launchingTaskId != null) {
- addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
- } else {
- logW("Starting desktop task launch without checking the task-limit")
- // TODO(b/378920066): This currently does not respect the desktop window limit.
- // It's possible that |launchingTaskId| is null when launching using an intent, and
- // the task-limit should be respected then too.
- null
- }
+ addAndGetMinimizeChanges(
+ displayId,
+ wct,
+ newTaskId = launchingTaskId,
+ launchingNewIntent = launchingTaskId == null,
+ )
val exitImmersiveResult =
desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -815,6 +866,10 @@ class DesktopTasksController(
if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId)
wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
+ }
+
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
}
@@ -885,7 +940,10 @@ class DesktopTasksController(
destinationBounds.height(),
displayController,
)
- toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
+ toggleResizeDesktopTaskTransitionHandler.startTransition(
+ wct,
+ interaction.animationStartBounds,
+ )
}
private fun dragToMaximizeDesktopTask(
@@ -916,6 +974,7 @@ class DesktopTasksController(
direction = ToggleTaskSizeInteraction.Direction.MAXIMIZE,
source = ToggleTaskSizeInteraction.Source.HEADER_DRAG_TO_TOP,
inputMethod = DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent),
+ animationStartBounds = currentDragBounds,
),
)
}
@@ -1203,9 +1262,12 @@ class DesktopTasksController(
moveHomeTask(wct, toTop = true)
// Currently, we only handle the desktop on the default display really.
- if (displayId == DEFAULT_DISPLAY && ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
+ if (
+ (displayId == DEFAULT_DISPLAY || Flags.enableBugFixesForSecondaryDisplay()) &&
+ ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ ) {
// Add translucent wallpaper activity to show the wallpaper underneath
- addWallpaperActivity(wct)
+ addWallpaperActivity(displayId, wct)
}
val expandedTasksOrderedFrontToBack = taskRepository.getExpandedTasksOrdered(displayId)
@@ -1254,7 +1316,7 @@ class DesktopTasksController(
?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) }
}
- private fun addWallpaperActivity(wct: WindowContainerTransaction) {
+ private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
val userHandle = UserHandle.of(userId)
val userContext = context.createContextAsUser(userHandle, /* flags= */ 0)
@@ -1265,6 +1327,9 @@ class DesktopTasksController(
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ launchDisplayId = displayId
+ }
}
val pendingIntent =
PendingIntent.getActivityAsUser(
@@ -1289,9 +1354,22 @@ class DesktopTasksController(
* Remove wallpaper activity if task provided is last task and wallpaper activity token is not
* null
*/
- private fun performDesktopExitCleanupIfNeeded(taskId: Int, wct: WindowContainerTransaction) {
- if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
- return
+ private fun performDesktopExitCleanupIfNeeded(
+ taskId: Int,
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ ) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (!taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)) {
+ return
+ }
+ if (displayId != DEFAULT_DISPLAY) {
+ return
+ }
+ } else {
+ if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+ return
+ }
}
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
@@ -1330,6 +1408,21 @@ class DesktopTasksController(
return false
}
+ override fun onTransitionConsumed(
+ transition: IBinder,
+ aborted: Boolean,
+ finishT: Transaction?,
+ ) {
+ pendingPipTransitionAndTask?.let { (pipTransition, taskId) ->
+ if (transition == pipTransition) {
+ if (aborted) {
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { minimizeTaskInner(it) }
+ }
+ pendingPipTransitionAndTask = null
+ }
+ }
+ }
+
override fun handleRequest(
transition: IBinder,
request: TransitionRequestInfo,
@@ -1483,7 +1576,10 @@ class DesktopTasksController(
if (!DesktopModeStatus.useRoundedCorners()) {
return
}
- val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ val cornerRadius =
+ context.resources
+ .getDimensionPixelSize(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ .toFloat()
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
@@ -1508,28 +1604,20 @@ class DesktopTasksController(
/** Open an existing instance of an app. */
fun openInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
- val wct = WindowContainerTransaction()
- val options = createNewWindowOptions(callingTask)
- if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
- wct.startTask(requestedTaskId, options.toBundle())
- val taskIdToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(
- callingTask.displayId,
- wct,
+ if (callingTask.isFreeform) {
+ val requestedTaskInfo = shellTaskOrganizer.getRunningTaskInfo(requestedTaskId)
+ if (requestedTaskInfo?.isFreeform == true) {
+ // If requested task is an already open freeform task, just move it to front.
+ moveTaskToFront(requestedTaskId)
+ } else {
+ moveBackgroundTaskToDesktop(
requestedTaskId,
+ WindowContainerTransaction(),
+ DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
)
- val exitResult =
- desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = callingTask.displayId,
- excludeTaskId = requestedTaskId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
- )
- val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
- addPendingAppLaunchTransition(transition, requestedTaskId, taskIdToMinimize)
- exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
+ }
} else {
+ val options = createNewWindowOptions(callingTask)
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
splitScreenController.startTask(
requestedTaskId,
@@ -1775,7 +1863,7 @@ class DesktopTasksController(
if (!isDesktopModeShowing(task.displayId)) return null
val wct = WindowContainerTransaction()
- performDesktopExitCleanupIfNeeded(task.taskId, wct)
+ performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
taskRepository.addClosingTask(task.displayId, task.taskId)
@@ -1858,7 +1946,7 @@ class DesktopTasksController(
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
}
- performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
+ performDesktopExitCleanupIfNeeded(taskInfo.taskId, taskInfo.displayId, wct)
}
private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
@@ -1892,17 +1980,21 @@ class DesktopTasksController(
// want it overridden in multi-window.
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
- performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
+ performDesktopExitCleanupIfNeeded(taskInfo.taskId, taskInfo.displayId, wct)
}
/** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
private fun addAndGetMinimizeChanges(
displayId: Int,
wct: WindowContainerTransaction,
- newTaskId: Int,
+ newTaskId: Int?,
+ launchingNewIntent: Boolean = false,
): Int? {
if (!desktopTasksLimiter.isPresent) return null
- return desktopTasksLimiter.get().addAndGetMinimizeTaskChanges(displayId, wct, newTaskId)
+ require(newTaskId == null || !launchingNewIntent)
+ return desktopTasksLimiter
+ .get()
+ .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
}
private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) {
@@ -2054,7 +2146,11 @@ class DesktopTasksController(
syncQueue,
taskInfo,
displayController,
- context,
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ displayController.getDisplayContext(taskInfo.displayId)
+ } else {
+ context
+ },
taskSurface,
rootTaskDisplayAreaOrganizer,
dragStartState,
@@ -2605,9 +2701,17 @@ class DesktopTasksController(
}
}
- override fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) {
+ override fun moveToDesktop(
+ taskId: Int,
+ transitionSource: DesktopModeTransitionSource,
+ remoteTransition: RemoteTransition?,
+ ) {
executeRemoteCallWithTaskPermission(controller, "moveTaskToDesktop") { c ->
- c.moveTaskToDesktop(taskId, transitionSource = transitionSource)
+ c.moveTaskToDesktop(
+ taskId,
+ transitionSource = transitionSource,
+ remoteTransition = remoteTransition,
+ )
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 635078e68a00..45faba6e341f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -214,12 +214,17 @@ class DesktopTasksLimiter(
fun addAndGetMinimizeTaskChanges(
displayId: Int,
wct: WindowContainerTransaction,
- newFrontTaskId: Int,
+ newFrontTaskId: Int?,
+ launchingNewIntent: Boolean = false,
): Int? {
logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
val taskRepository = desktopUserRepositories.current
val taskIdToMinimize =
- getTaskIdToMinimize(taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
+ getTaskIdToMinimize(
+ taskRepository.getExpandedTasksOrdered(displayId),
+ newFrontTaskId,
+ launchingNewIntent,
+ )
// If it's a running task, reorder it to back.
taskIdToMinimize
?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
@@ -242,15 +247,24 @@ class DesktopTasksLimiter(
* Returns the minimized task from the list of visible tasks ordered from front to back with the
* new task placed in front of other tasks.
*/
- fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>, newTaskIdInFront: Int? = null): Int? {
+ fun getTaskIdToMinimize(
+ visibleOrderedTasks: List<Int>,
+ newTaskIdInFront: Int? = null,
+ launchingNewIntent: Boolean = false,
+ ): Int? {
return getTaskIdToMinimize(
- createOrderedTaskListWithGivenTaskInFront(visibleOrderedTasks, newTaskIdInFront)
+ createOrderedTaskListWithGivenTaskInFront(visibleOrderedTasks, newTaskIdInFront),
+ launchingNewIntent,
)
}
/** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
- private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? {
- if (visibleOrderedTasks.size <= maxTasksLimit) {
+ private fun getTaskIdToMinimize(
+ visibleOrderedTasks: List<Int>,
+ launchingNewIntent: Boolean,
+ ): Int? {
+ val newTasksOpening = if (launchingNewIntent) 1 else 0
+ if (visibleOrderedTasks.size + newTasksOpening <= maxTasksLimit) {
logV("No need to minimize; tasks below limit")
// No need to minimize anything
return null
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 e5f52839d9f4..8b5d1c502bc9 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
@@ -28,6 +28,7 @@ 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.annotations.ShellMainThread
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
@@ -36,6 +37,7 @@ import kotlinx.coroutines.CoroutineScope
class DesktopUserRepositories(
context: Context,
shellInit: ShellInit,
+ private val shellController: ShellController,
private val persistentRepository: DesktopPersistentRepository,
private val repositoryInitializer: DesktopRepositoryInitializer,
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
@@ -61,15 +63,16 @@ class DesktopUserRepositories(
init {
userId = ActivityManager.getCurrentUser()
if (DesktopModeStatus.canEnterDesktopMode(context)) {
- shellInit.addInitCallback(::initRepoFromPersistentStorage, this)
+ shellInit.addInitCallback(::onInit, this)
}
if (Flags.enableDesktopWindowingHsum()) {
userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
}
}
- private fun initRepoFromPersistentStorage() {
+ private fun onInit() {
repositoryInitializer.initialize(this)
+ shellController.addUserChangeListener(this)
}
/** Returns [DesktopRepository] for the parent user id. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 11110f19e6d6..d23c9d0b8ffd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -19,7 +19,6 @@ import android.content.Intent
import android.content.Intent.FILL_IN_COMPONENT
import android.graphics.PointF
import android.graphics.Rect
-import android.os.Bundle
import android.os.IBinder
import android.os.SystemClock
import android.os.SystemProperties
@@ -139,7 +138,11 @@ sealed class DragToDesktopTransitionHandler(
taskUser,
)
val wct = WindowContainerTransaction()
- wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
+ // The app that is being dragged into desktop mode might cause new transitions, make this
+ // launch transient to make sure those transitions can execute in parallel and thus won't
+ // block the end-drag transition.
+ val intentOptions = ActivityOptions.makeBasic().setTransientLaunch()
+ wct.sendPendingIntent(pendingIntent, launchHomeIntent, intentOptions.toBundle())
val startTransitionToken =
transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index aac2361f717e..fa383cb55118 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -53,7 +53,8 @@ interface IDesktopMode {
oneway void setTaskListener(IDesktopTaskListener listener);
/** Move a task with given `taskId` to desktop */
- void moveToDesktop(int taskId, in DesktopModeTransitionSource transitionSource);
+ void moveToDesktop(int taskId, in DesktopModeTransitionSource transitionSource,
+ in @nullable RemoteTransition remoteTransition);
/** Remove desktop on the given display */
oneway void removeDesktop(int displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
index 7afd8d7f6e48..f6ebf7221e82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.desktopmode.common
+import android.graphics.Rect
import com.android.internal.jank.Cuj
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -23,10 +24,13 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction.Ambiguo
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction.Source
/** Represents a user interaction to toggle a desktop task's size from to maximize or vice versa. */
-data class ToggleTaskSizeInteraction(
+data class ToggleTaskSizeInteraction
+@JvmOverloads
+constructor(
val direction: Direction,
val source: Source,
val inputMethod: InputMethod,
+ val animationStartBounds: Rect? = null,
) {
constructor(
isMaximized: Boolean,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
index c5fca028a1a6..45d1281ba0e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -23,7 +23,6 @@ import android.content.res.Resources
import android.graphics.Point
import android.os.SystemProperties
import android.util.Slog
-import androidx.core.content.withStyledAttributes
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.CaptionState
@@ -299,31 +298,12 @@ class AppHandleEducationController(
}
private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme {
- context.withStyledAttributes(
- set = null,
- attrs =
- intArrayOf(
- com.android.internal.R.attr.materialColorOnTertiaryFixed,
- com.android.internal.R.attr.materialColorTertiaryFixed,
- com.android.internal.R.attr.materialColorTertiaryFixedDim,
- ),
- defStyleAttr = 0,
- defStyleRes = 0,
- ) {
- val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0)
- val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0)
- val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0)
- val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo
+ val onTertiaryFixed =
+ context.getColor(com.android.internal.R.color.materialColorOnTertiaryFixed)
+ val tertiaryFixed =
+ context.getColor(com.android.internal.R.color.materialColorTertiaryFixed)
- val tooltipContainerColor =
- if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) {
- tertiaryFixed
- } else {
- tertiaryFixedDim
- }
- return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed)
- }
- return TooltipColorScheme(0, 0, 0)
+ return TooltipColorScheme(tertiaryFixed, onTertiaryFixed, onTertiaryFixed)
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
index 9d015357b60b..837a6dd32ff2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
@@ -36,7 +36,8 @@ the product.
thread)
- This is always another thread even if config_enableShellMainThread is not set true
- **Note**:
- - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
+ - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority but can be requested to be boosted
+ to `THREAD_PRIORITY_FOREGROUND`
- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
animations could be offloaded here)
- `ShellSplashScreenThread` (only for use with splashscreens)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 2ae9828ca0db..52b6c62b0721 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -99,6 +100,12 @@ public class FreeformTaskTransitionHandler
return token;
}
+ @Override
+ public IBinder startPipTransition(WindowContainerTransaction wct) {
+ final IBinder token = mTransitions.startTransition(TRANSIT_PIP, wct, null);
+ mPendingTransitionTokens.add(token);
+ return token;
+ }
@Override
public IBinder startRemoveTransition(WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
index 5984d486f838..a874a5be426d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
@@ -51,4 +51,13 @@ public interface FreeformTaskTransitionStarter {
* @return the started transition
*/
IBinder startRemoveTransition(WindowContainerTransaction wct);
+
+ /**
+ * Starts PiP transition
+ *
+ * @param wct the {@link WindowContainerTransaction} that launches the PiP
+ *
+ * @return the started transition
+ */
+ IBinder startPipTransition(WindowContainerTransaction wct);
} \ No newline at end of file
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 6129651dc271..6e7740dc13e3 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
@@ -39,6 +39,7 @@ 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;
@@ -312,6 +313,23 @@ 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/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 582df486b2b3..9c3e815b389d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -562,6 +562,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
e.printStackTrace();
}
+ mAppOpsListener.setCallback(mTouchHandler.getMotionHelper());
+
// Handle for system task stack changes.
mTaskStackListener.addListener(
new TaskStackListenerCallback() {
@@ -779,6 +781,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// cancel any running animator, as it is using stale display layout information
animator.cancel();
}
+ mMenuController.hideMenu();
onDisplayChangedUncheck(layout, saveRestoreSnapFraction);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 8ac7f89d8f8a..56a158b0c1c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -269,6 +269,8 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mShellController.addConfigurationChangeListener(this);
mShellController.addUserChangeListener(this);
+
+ mAppOpsListener.setCallback(mPipTaskOrganizer::removePip);
}
@Override
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 19428ee39919..8c6d5f5c6660 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
@@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -54,6 +55,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -94,12 +96,14 @@ public class PipController implements ConfigurationChangeListener,
private final ShellTaskOrganizer mShellTaskOrganizer;
private final PipTransitionState mPipTransitionState;
private final PipTouchHandler mPipTouchHandler;
+ private final PipAppOpsListener mPipAppOpsListener;
+ private final PhonePipMenuController mPipMenuController;
private final ShellExecutor mMainExecutor;
private final PipImpl mImpl;
private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
// Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
- private PipAnimationListener mPipRecentsAnimationListener;
+ @Nullable private PipAnimationListener mPipRecentsAnimationListener;
@VisibleForTesting
interface PipAnimationListener {
@@ -137,6 +141,8 @@ public class PipController implements ConfigurationChangeListener,
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
PipTouchHandler pipTouchHandler,
+ PipAppOpsListener pipAppOpsListener,
+ PhonePipMenuController pipMenuController,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
@@ -152,6 +158,8 @@ public class PipController implements ConfigurationChangeListener,
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipTouchHandler = pipTouchHandler;
+ mPipAppOpsListener = pipAppOpsListener;
+ mPipMenuController = pipMenuController;
mMainExecutor = mainExecutor;
mImpl = new PipImpl();
@@ -177,6 +185,8 @@ public class PipController implements ConfigurationChangeListener,
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
PipTouchHandler pipTouchHandler,
+ PipAppOpsListener pipAppOpsListener,
+ PhonePipMenuController pipMenuController,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -186,7 +196,8 @@ public class PipController implements ConfigurationChangeListener,
return new PipController(context, shellInit, shellCommandHandler, shellController,
displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
- pipTransitionState, pipTouchHandler, mainExecutor);
+ pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
+ mainExecutor);
}
public PipImpl getPipImpl() {
@@ -225,6 +236,20 @@ public class PipController implements ConfigurationChangeListener,
mPipScheduler.scheduleExitPipViaExpand();
}
});
+
+ mPipAppOpsListener.setCallback(mPipTouchHandler.getMotionHelper());
+ mPipTransitionState.addPipTransitionStateChangedListener(
+ (oldState, newState, extra) -> {
+ if (newState == PipTransitionState.ENTERED_PIP) {
+ final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
+ if (taskInfo != null && taskInfo.topActivity != null) {
+ mPipAppOpsListener.onActivityPinned(
+ taskInfo.topActivity.getPackageName());
+ }
+ } else if (newState == PipTransitionState.EXITED_PIP) {
+ mPipAppOpsListener.onActivityUnpinned();
+ }
+ });
}
private ExternalInterfaceBinder createExternalInterface() {
@@ -309,6 +334,7 @@ public class PipController implements ConfigurationChangeListener,
}
mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
+ mPipMenuController.hideMenu();
if (mPipTransitionState.isInFixedRotation()) {
// Do not change the bounds when in fixed rotation, but do update the movement bounds
@@ -378,7 +404,9 @@ public class PipController implements ConfigurationChangeListener,
tx.setLayer(overlay, Integer.MAX_VALUE);
tx.apply();
}
- mPipRecentsAnimationListener.onPipAnimationStarted();
+ if (mPipRecentsAnimationListener != null) {
+ mPipRecentsAnimationListener.onPipAnimationStarted();
+ }
}
private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 44cc563eadf4..fc3fbe299605 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -222,7 +222,10 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
menuController, mainExecutor,
mPipPerfHintController);
- mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
+ mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
+ updateMinMaxSize(aspectRatio);
+ onAspectRatioChanged();
+ });
mMoveOnShelVisibilityChanged = () -> {
if (mIsImeShowing && mImeHeight > mShelfHeight) {
@@ -768,16 +771,17 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
private void animateToNormalSize(Runnable callback) {
// Save the current bounds as the user-resize bounds.
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
+ final Rect adjustedNormalBounds = getAdjustedNormalBounds();
+ mSavedSnapFraction = mMotionHelper.animateToExpandedState(adjustedNormalBounds,
+ getMovementBounds(mPipBoundsState.getBounds()),
+ getMovementBounds(adjustedNormalBounds), callback /* callback */);
+ }
+ private Rect getAdjustedNormalBounds() {
final Size minMenuSize = mMenuController.getEstimatedMinMenuSize();
final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
final Rect normalBounds = new Rect(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
- final Rect adjustedNormalBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(
- normalBounds, minMenuSize);
-
- mSavedSnapFraction = mMotionHelper.animateToExpandedState(adjustedNormalBounds,
- getMovementBounds(mPipBoundsState.getBounds()),
- getMovementBounds(adjustedNormalBounds), callback /* callback */);
+ return mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize);
}
private void animateToUnexpandedState(Rect restoreBounds) {
@@ -1065,6 +1069,10 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
insetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
mMotionHelper.onMovementBoundsChanged();
+
+ if (mPipResizeGestureHandler.getUserResizeBounds().isEmpty()) {
+ mPipResizeGestureHandler.setUserResizeBounds(getAdjustedNormalBounds());
+ }
}
private Rect getMovementBounds(Rect curBounds) {
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 b171db2c3793..acb5622b041c 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
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
@@ -30,6 +31,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -44,6 +46,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
@@ -52,6 +55,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -120,7 +124,6 @@ public class PipTransition extends PipTransitionController implements
// Internal state and relevant cached info
//
- @Nullable
private Transitions.TransitionFinishCallback mFinishCallback;
private ValueAnimator mTransitionAnimator;
@@ -193,7 +196,7 @@ public class PipTransition extends PipTransitionController implements
@NonNull TransitionRequestInfo request) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
mEnterTransition = transition;
- return getEnterPipTransaction(transition, request);
+ return getEnterPipTransaction(transition, request.getPipChange());
}
return null;
}
@@ -202,7 +205,8 @@ public class PipTransition extends PipTransitionController implements
public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request,
@NonNull WindowContainerTransaction outWct) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
- outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */);
+ outWct.merge(getEnterPipTransaction(transition, request.getPipChange()),
+ true /* transfer */);
mEnterTransition = transition;
}
}
@@ -231,7 +235,6 @@ public class PipTransition extends PipTransitionController implements
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- mFinishCallback = finishCallback;
if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
mEnterTransition = null;
// If we are in swipe PiP to Home transition we are ENTERING_PIP as a jumpcut transition
@@ -277,11 +280,35 @@ public class PipTransition extends PipTransitionController implements
if (isRemovePipTransition(info)) {
return removePipImmediately(info, startTransaction, finishTransaction, finishCallback);
}
- mFinishCallback = null;
return false;
}
@Override
+ public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ // TRANSIT_TO_FRONT, though uncommon with triggering PiP, should semantically also
+ // be allowed to animate if the task in question is pinned already - see b/308054074.
+ if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN
+ || transitType == TRANSIT_TO_FRONT) {
+ return true;
+ }
+ // This can happen if the request to enter PIP happens when we are collecting for
+ // another transition, such as TRANSIT_CHANGE (display rotation).
+ if (transitType == TRANSIT_CHANGE) {
+ return true;
+ }
+
+ // Please file a bug to handle the unexpected transition type.
+ android.util.Slog.e(TAG, "Found new PIP in transition with mis-matched type="
+ + transitTypeToString(transitType), new Throwable());
+ }
+ return false;
+ }
+
+
+ @Override
public void end() {
if (mTransitionAnimator != null && mTransitionAnimator.isRunning()) {
mTransitionAnimator.end();
@@ -301,6 +328,7 @@ public class PipTransition extends PipTransitionController implements
if (pipChange == null) {
return false;
}
+ 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,
@@ -348,6 +376,7 @@ public class PipTransition extends PipTransitionController implements
if (pipActivityChange == null) {
return false;
}
+ mFinishCallback = finishCallback;
final SurfaceControl pipLeash = getLeash(pipChange);
final Rect destinationBounds = pipChange.getEndAbsBounds();
@@ -416,6 +445,7 @@ public class PipTransition extends PipTransitionController implements
if (pipActivityChange == null) {
return false;
}
+ mFinishCallback = finishCallback;
final SurfaceControl pipLeash = getLeash(pipChange);
final Rect startBounds = pipChange.getStartAbsBounds();
@@ -542,6 +572,7 @@ public class PipTransition extends PipTransitionController implements
if (pipChange == null) {
return false;
}
+ mFinishCallback = finishCallback;
Rect destinationBounds = pipChange.getEndAbsBounds();
SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
@@ -584,6 +615,7 @@ public class PipTransition extends PipTransitionController implements
return false;
}
}
+ mFinishCallback = finishCallback;
// The parent change if we were in a multi-activity PiP; null if single activity PiP.
final TransitionInfo.Change parentBeforePip = pipChange.getTaskInfo() == null
@@ -662,19 +694,6 @@ public class PipTransition extends PipTransitionController implements
}
@Nullable
- private TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
- @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;
- }
-
- @Nullable
private TransitionInfo.Change getChangeByToken(TransitionInfo info,
WindowContainerToken token) {
for (TransitionInfo.Change change : info.getChanges()) {
@@ -713,6 +732,10 @@ public class PipTransition extends PipTransitionController implements
&& getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) {
adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top);
}
+ if (Flags.enableDesktopWindowingPip()) {
+ adjustedSourceRectHint.offset(-pipActivityChange.getStartAbsBounds().left,
+ -pipActivityChange.getStartAbsBounds().top);
+ }
} else {
// For non-valid app provided src-rect-hint, calculate one to crop into during
// app icon overlay animation.
@@ -760,9 +783,9 @@ public class PipTransition extends PipTransitionController implements
}
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
+ @NonNull TransitionRequestInfo.PipChange pipChange) {
// cache the original task token to check for multi-activity case later
- final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
+ final ActivityManager.RunningTaskInfo pipTask = pipChange.getTaskInfo();
PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
mPipTaskListener.setPictureInPictureParams(pipParams);
mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
@@ -772,14 +795,18 @@ public class PipTransition extends PipTransitionController implements
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setBounds(entryBounds);
+ // Operate on the TF token in case we are dealing with AE case; this should avoid marking
+ // activities in other TFs as config-at-end.
+ WindowContainerToken token = pipChange.getTaskFragmentToken();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
- wct.deferConfigToTransitionEnd(pipTask.token);
+ wct.movePipActivityToPinnedRootTask(token, entryBounds);
+ wct.deferConfigToTransitionEnd(token);
return wct;
}
private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
- final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask();
+ final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipChange() != null
+ ? requestInfo.getPipChange().getTaskInfo() : null;
if (pipTask == null) {
return false;
}
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 b922cd029468..0869caa55369 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
@@ -550,7 +550,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- if (Flags.enableRefactorTaskThumbnail() && isWallpaperTask(taskInfo)) {
+ if (
+ Flags.enableUseTopVisibleActivityForExcludeFromRecentTask()
+ && isWallpaperTask(taskInfo)) {
// Don't add the wallpaper task as an entry in grouped tasks
continue;
}
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 37d58780fc85..032dac9ff3a2 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
@@ -1317,6 +1317,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
// otherwise a new transition will notify the relevant observers
if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
+ } else if (!toHome && mState == STATE_NEW_TASK
+ && allAppsAreTranslucent(mOpeningTasks)) {
+ // We are opening a translucent app. Launcher is still visible so we do nothing.
} else if (!toHome) {
// For some transitions, we may have notified home activity that it became
// visible. We need to notify the observer that we are no longer going home.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index a368245db25f..39ed2061c675 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -185,6 +185,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
private final LauncherApps mLauncherApps;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
+ private final ShellExecutor mBgExecutor;
private final Handler mMainHandler;
private final SplitScreenImpl mImpl = new SplitScreenImpl();
private final DisplayController mDisplayController;
@@ -231,7 +232,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
MultiInstanceHelper multiInstanceHelper,
SplitState splitState,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ ShellExecutor bgExecutor) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
@@ -241,6 +243,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -298,8 +301,9 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
- mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
- mWindowDecorViewModel, mSplitState);
+ mMainExecutor, mMainHandler, mBgExecutor, mRecentTasksOptional,
+ mLaunchAdjacentController, mWindowDecorViewModel, mSplitState,
+ mDesktopTasksController);
}
@Override
@@ -512,6 +516,11 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
+ /** Get the parent-based coordinates for split stages. */
+ public void getRefStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ mStageCoordinator.getRefStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
+ }
+
public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.registerSplitScreenListener(listener);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b40996f52bd3..164dbe97c1f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -20,6 +20,7 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_A
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+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 android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -141,10 +142,12 @@ import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.split.SplitWindowManager;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex;
@@ -213,12 +216,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final SplitscreenEventLogger mLogger;
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
+ private final ShellExecutor mBgExecutor;
// Cache live tile tasks while entering recents, evict them from stages in finish transaction
// if user is opening another task(s).
private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
private final Optional<RecentTasksController> mRecentTasks;
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
+ private final Optional<DesktopTasksController> mDesktopTasksController;
/** Singleton source of truth for the current state of split screen on this device. */
private final SplitState mSplitState;
@@ -340,14 +345,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
};
- protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, DisplayController displayController,
+ protected StageCoordinator(Context context,
+ int displayId,
+ SyncTransactionQueue syncQueue,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
- Handler mainHandler, Optional<RecentTasksController> recentTasks,
+ DisplayInsetsController displayInsetsController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ ShellExecutor mainExecutor,
+ Handler mainHandler,
+ ShellExecutor bgExecutor,
+ Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+ Optional<DesktopTasksController> desktopTasksController) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -355,10 +369,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
mSplitState = splitState;
+ mDesktopTasksController = desktopTasksController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
@@ -370,6 +386,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
this /*stageListenerCallbacks*/,
mSyncQueue,
iconProvider,
+ mMainExecutor,
+ mBgExecutor,
mWindowDecorViewModel);
} else {
mMainStage = new StageTaskListener(
@@ -379,6 +397,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
this /*stageListenerCallbacks*/,
mSyncQueue,
iconProvider,
+ mMainExecutor,
+ mBgExecutor,
mWindowDecorViewModel, STAGE_TYPE_MAIN);
mSideStage = new StageTaskListener(
mContext,
@@ -387,6 +407,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
this /*stageListenerCallbacks*/,
mSyncQueue,
iconProvider,
+ mMainExecutor,
+ mBgExecutor,
mWindowDecorViewModel, STAGE_TYPE_SIDE);
}
mDisplayController = displayController;
@@ -408,15 +430,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@VisibleForTesting
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
- StageTaskListener sideStage, DisplayController displayController,
+ StageCoordinator(Context context,
+ int displayId,
+ SyncTransactionQueue syncQueue,
+ ShellTaskOrganizer taskOrganizer,
+ StageTaskListener mainStage,
+ StageTaskListener sideStage,
+ DisplayController displayController,
DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
- Handler mainHandler, Optional<RecentTasksController> recentTasks,
+ DisplayInsetsController displayInsetsController,
+ SplitLayout splitLayout,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ ShellExecutor mainExecutor,
+ Handler mainHandler,
+ ShellExecutor bgExecutor,
+ Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+ Optional<DesktopTasksController> desktopTasksController) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -433,10 +465,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
mSplitState = splitState;
+ mDesktopTasksController = desktopTasksController;
mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
@@ -1776,6 +1810,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
outBottomOrRightBounds.set(mSplitLayout.getBottomRightBounds());
}
+ void getRefStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ outTopOrLeftBounds.set(mSplitLayout.getTopLeftRefBounds());
+ outBottomOrRightBounds.set(mSplitLayout.getBottomRightRefBounds());
+ }
+
private void runForActiveStages(Consumer<StageTaskListener> consumer) {
mStageOrderOperator.getActiveStages().forEach(consumer);
}
@@ -1964,32 +2003,32 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
-
- if (Flags.enableFlexibleTwoAppSplit()) {
- // Split screen can be laid out in such a way that some of the apps are offscreen.
- // For the purposes of passing SplitBounds up to launcher (for use in thumbnails
- // etc.), we crop the bounds down to the screen size.
- topLeftBounds.left =
- Math.max(topLeftBounds.left, 0);
- topLeftBounds.top =
- Math.max(topLeftBounds.top, 0);
- bottomRightBounds.right =
- Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
- bottomRightBounds.top =
- Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
-
- // TODO (b/349828130): Can change to getState() fully after brief soak time.
- if (mSplitState.get() != currentSnapPosition) {
- Log.wtf(TAG, "SplitState is " + mSplitState.get()
- + ", expected " + currentSnapPosition);
- currentSnapPosition = mSplitState.get();
+ // If all stages are filled, create new SplitBounds and update Recents.
+ if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
+ int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
+ if (Flags.enableFlexibleTwoAppSplit()) {
+ // Split screen can be laid out in such a way that some of the apps are
+ // offscreen. For the purposes of passing SplitBounds up to launcher (for use in
+ // thumbnails etc.), we crop the bounds down to the screen size.
+ topLeftBounds.left =
+ Math.max(topLeftBounds.left, 0);
+ topLeftBounds.top =
+ Math.max(topLeftBounds.top, 0);
+ bottomRightBounds.right =
+ Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
+ bottomRightBounds.top =
+ Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+
+ // TODO (b/349828130): Can change to getState() fully after brief soak time.
+ if (mSplitState.get() != currentSnapPosition) {
+ Log.wtf(TAG, "SplitState is " + mSplitState.get()
+ + ", expected " + currentSnapPosition);
+ currentSnapPosition = mSplitState.get();
+ }
}
- }
+ SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
+ leftTopTaskId, rightBottomTaskId, currentSnapPosition);
- SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
- leftTopTaskId, rightBottomTaskId, currentSnapPosition);
- if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
splitBounds);
@@ -2150,8 +2189,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.setForceTranslucent(mRootTaskInfo.token, translucent);
}
- /** Callback when split roots visiblility changed.
- * NOTICE: This only be called on legacy transition. */
+ /** Callback when split roots visiblility changed. */
@Override
public void onStageVisibilityChanged(StageTaskListener stageListener) {
// If split didn't active, just ignore this callback because we should already did these
@@ -2736,9 +2774,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final @WindowManager.TransitionType int type = request.getType();
final boolean isOpening = isOpeningType(type);
final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ final boolean inDesktopMode = mDesktopTasksController.isPresent()
+ && mDesktopTasksController.get().isDesktopModeShowing(mDisplayId);
+ final boolean isLaunchingDesktopTask = isOpening && DesktopModeStatus.canEnterDesktopMode(
+ mContext) && triggerTask.getWindowingMode() == WINDOWING_MODE_FREEFORM;
final StageTaskListener stage = getStageOfTask(triggerTask);
- if (isOpening && inFullscreen) {
+ if (inDesktopMode || isLaunchingDesktopTask) {
+ // Don't handle request when desktop mode is showing (since they don't coexist), or
+ // when launching a desktop task (defer to DesktopTasksController)
+ return null;
+ } else if (isOpening && inFullscreen) {
// One task is opening into fullscreen mode, remove the corresponding split record.
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
logExit(EXIT_REASON_FULLSCREEN_REQUEST);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
index 3fa8df40dfef..5256e7867499 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
@@ -20,6 +20,7 @@ import android.content.Context
import com.android.internal.protolog.ProtoLog
import com.android.launcher3.icons.IconProvider
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -52,6 +53,8 @@ class StageOrderOperator (
stageCallbacks: StageTaskListener.StageListenerCallbacks,
syncQueue: SyncTransactionQueue,
iconProvider: IconProvider,
+ mainExecutor: ShellExecutor,
+ bgExecutor: ShellExecutor,
windowDecorViewModel: Optional<WindowDecorViewModel>
) {
@@ -83,6 +86,8 @@ class StageOrderOperator (
stageCallbacks,
syncQueue,
iconProvider,
+ mainExecutor,
+ bgExecutor,
windowDecorViewModel,
stageIds[i])
)
@@ -95,13 +100,16 @@ class StageOrderOperator (
*/
fun onEnteringSplit(@SnapPosition goingToLayout: Int) {
if (goingToLayout == currentLayout) {
- // Add protolog here. Return for now, but maybe we want to handle swap case, TBD
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Entering Split requested same layout split is in: %d", goingToLayout)
return
}
val freeStages: List<StageTaskListener> =
allStages.filterNot { activeStages.contains(it) }
when(goingToLayout) {
- SplitScreenConstants.SNAP_TO_2_50_50 -> {
+ SplitScreenConstants.SNAP_TO_2_50_50,
+ SplitScreenConstants.SNAP_TO_2_33_66,
+ SplitScreenConstants.SNAP_TO_2_66_33 -> {
if (activeStages.size < 2) {
// take from allStages and add into activeStages
for (i in 0 until (2 - activeStages.size)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 4a37169add36..816f51f997d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -48,6 +48,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
@@ -95,6 +96,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private final StageListenerCallbacks mCallbacks;
private final SyncTransactionQueue mSyncQueue;
private final IconProvider mIconProvider;
+ private final ShellExecutor mMainExecutor;
+ private final ShellExecutor mBgExecutor;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
/** Whether or not the root task has been created. */
@@ -111,14 +114,21 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// TODO(b/204308910): Extracts SplitDecorManager related code to common package.
private SplitDecorManager mSplitDecorManager;
- StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
+ StageTaskListener(Context context,
+ ShellTaskOrganizer taskOrganizer,
+ int displayId,
+ StageListenerCallbacks callbacks,
+ SyncTransactionQueue syncQueue,
IconProvider iconProvider,
+ ShellExecutor mainExecutor,
+ ShellExecutor bgExecutor,
Optional<WindowDecorViewModel> windowDecorViewModel, int id) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mIconProvider = iconProvider;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
mId = id;
@@ -214,9 +224,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
- mSplitDecorManager = new SplitDecorManager(
- mRootTaskInfo.configuration,
- mIconProvider);
+ mSplitDecorManager = new SplitDecorManager(mRootTaskInfo.configuration, mIconProvider,
+ mMainExecutor, mBgExecutor);
mHasRootTask = true;
mCallbacks.onRootTaskAppeared();
if (mVisible != mRootTaskInfo.isVisible) {
@@ -240,12 +249,20 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s "
- + "stageId=%s",
- taskInfo.taskId, taskInfo.baseActivity, stageTypeToString(mId));
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onTaskInfoChanged: taskId=%d vis=%b reqVis=%b baseAct=%s stageId=%s",
+ taskInfo.taskId, taskInfo.isVisible, taskInfo.isVisibleRequested,
+ taskInfo.baseActivity, stageTypeToString(mId));
mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
if (mRootTaskInfo.taskId == taskInfo.taskId) {
mRootTaskInfo = taskInfo;
+ boolean isVisible = taskInfo.isVisible && taskInfo.isVisibleRequested;
+ if (mVisible != isVisible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: currentVis=%b newVis=%b",
+ mVisible, isVisible);
+ mVisible = isVisible;
+ mCallbacks.onStageVisibilityChanged(this);
+ }
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
if (!taskInfo.supportsMultiWindow
|| !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
@@ -260,7 +277,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return;
}
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
- mVisible = isStageVisible();
mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
taskInfo.isVisible && taskInfo.isVisibleRequested);
} else {
@@ -309,19 +325,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
t.reparent(sc, findTaskSurface(taskId));
}
- /**
- * Checks against all children task info and return true if any are marked as visible.
- */
- private boolean isStageVisible() {
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- if (mChildrenTaskInfo.valueAt(i).isVisible
- && mChildrenTaskInfo.valueAt(i).isVisibleRequested) {
- return true;
- }
- }
- return false;
- }
-
private SurfaceControl findTaskSurface(int taskId) {
if (mRootTaskInfo.taskId == taskId) {
return mRootLeash;
@@ -350,12 +353,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
- void screenshotIfNeeded(SurfaceControl.Transaction t) {
- if (mSplitDecorManager != null) {
- mSplitDecorManager.screenshotIfNeeded(t);
- }
- }
-
void fadeOutDecor(Runnable finishedCallback) {
if (mSplitDecorManager != null) {
mSplitDecorManager.fadeOutDecor(finishedCallback, false /* addDelay */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index c5e158c6b452..ea7553061137 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -53,6 +53,7 @@ public class TvSplitScreenController extends SplitScreenController {
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
private final ShellExecutor mMainExecutor;
+ private final ShellExecutor mBgExecutor;
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
@@ -85,18 +86,20 @@ public class TvSplitScreenController extends SplitScreenController {
SplitState splitState,
ShellExecutor mainExecutor,
Handler mainHandler,
+ ShellExecutor bgExecutor,
SystemWindows systemWindows) {
super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
- mainExecutor, mainHandler);
-
+ mainExecutor, mainHandler, bgExecutor);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -106,8 +109,6 @@ public class TvSplitScreenController extends SplitScreenController {
mRecentTasksOptional = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mSplitState = splitState;
-
- mMainHandler = mainHandler;
mSystemWindows = systemWindows;
}
@@ -120,7 +121,7 @@ public class TvSplitScreenController extends SplitScreenController {
return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool,
- mIconProvider, mMainExecutor, mMainHandler,
+ mIconProvider, mMainExecutor, mMainHandler, mBgExecutor,
mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index ef1f88e635d3..9d85bea421ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -51,15 +51,15 @@ public class TvStageCoordinator extends StageCoordinator
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
- Handler mainHandler,
+ Handler mainHandler, ShellExecutor bgExecutor,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
SplitState splitState,
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty(),
- splitState);
+ mainExecutor, mainHandler, bgExecutor, recentTasks, launchAdjacentController,
+ Optional.empty(), splitState, Optional.empty());
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 82c0aaf3bc8b..361d766370e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -186,6 +186,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
*/
public void setObscuredTouchRect(Rect obscuredRect) {
mObscuredTouchRegion = obscuredRect != null ? new Region(obscuredRect) : null;
+ invalidate();
}
/**
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 92d1f9c26bbc..41c0a11827bb 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
@@ -287,6 +287,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
// Postpone transition splitting to later.
WindowContainerTransaction out = new WindowContainerTransaction();
+ mPipHandler.augmentRequest(transition, request, out);
return out;
} else if (request.getRemoteTransition() != null
&& TransitionUtil.isOpeningType(request.getType())
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 3d3de88cdafc..b0547a2a47b1 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.transition;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
@@ -37,6 +38,8 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+import java.util.List;
+
class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
private final UnfoldTransitionHandler mUnfoldHandler;
private final ActivityEmbeddingController mActivityEmbeddingController;
@@ -127,6 +130,13 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
}
}
+ TransitionInfo.Change pipActivityChange = null;
+ if (pipChange != null) {
+ pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+ info, pipChange.getContainer());
+ everythingElse.getChanges().remove(pipActivityChange);
+ }
+
final Transitions.TransitionFinishCallback finishCB = (wct) -> {
--mInFlightSubAnimations;
joinFinishArgs(wct);
@@ -139,13 +149,23 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
return false;
}
- // PIP window should always be on the highest Z order.
- if (pipChange != null) {
+ if (pipChange != null && pipActivityChange == null) {
+ // We are operating on a single PiP task for the enter animation here.
mInFlightSubAnimations = 2;
+ // PIP window should always be on the highest Z order.
mPipHandler.startEnterAnimation(
pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
finishTransaction,
finishCB);
+ } else if (pipActivityChange != null) {
+ // If there is both a PiP task and a PiP config-at-end activity change,
+ // put them into a separate TransitionInfo, and send to be animated as TRANSIT_PIP.
+ mInFlightSubAnimations = 2;
+ TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
+ pipInfo.getChanges().addAll(List.of(pipChange, pipActivityChange));
+ mPipHandler.startAnimation(mTransition, pipInfo,
+ startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+ finishTransaction, finishCB);
} else {
mInFlightSubAnimations = 1;
}
@@ -184,6 +204,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"tryAnimateOpenIntentWithRemoteAndPipOrDesktop");
TransitionInfo.Change pipChange = null;
+ TransitionInfo.Change pipActivityChange = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
TransitionInfo.Change change = info.getChanges().get(i);
if (mPipHandler.isEnteringPip(change, info.getType())) {
@@ -193,6 +214,12 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
}
pipChange = change;
info.getChanges().remove(i);
+ } else if (change.getTaskInfo() == null && change.getParent() != null
+ && pipChange != null && change.getParent().equals(pipChange.getContainer())) {
+ // Cache the PiP activity if it's a target and cached pip task change is its parent;
+ // note that we are bottom-to-top, so if such activity has a task
+ // that is also a target, then it must have been cached already as pipChange.
+ pipActivityChange = change;
}
}
TransitionInfo.Change desktopChange = null;
@@ -237,8 +264,16 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
// make a new startTransaction because pip's startEnterAnimation "consumes" it so
// we need a separate one to send over to launcher.
SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
-
- mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
+ if (pipActivityChange == null) {
+ mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction,
+ finishCB);
+ } else {
+ info.getChanges().remove(pipActivityChange);
+ TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
+ pipInfo.getChanges().addAll(List.of(pipChange, pipActivityChange));
+ mPipHandler.startAnimation(mTransition, pipInfo, startTransaction,
+ finishTransaction, finishCB);
+ }
// Dispatch the rest of the transition normally.
if (mLeftoversHandler != null
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 c385f9afcf3a..1c7e62b71218 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
@@ -76,8 +76,8 @@ public class HomeTransitionObserver implements TransitionObserver,
if (Flags.migratePredictiveBackTransition()) {
final boolean gestureToHomeTransition = isBackGesture
&& TransitionUtil.isClosingType(info.getType());
- if (gestureToHomeTransition
- || (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode))) {
+ if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
+ || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
notifyHomeVisibilityChanged(gestureToHomeTransition
|| TransitionUtil.isOpeningType(mode));
}
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 611f3e0ac5e8..a7d6301ecf06 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
@@ -1076,9 +1076,11 @@ public class Transitions implements RemoteCallable<Transitions>,
@Nullable TransitionHandler skip
) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
- if (mHandlers.get(i) == skip) continue;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
- mHandlers.get(i));
+ if (mHandlers.get(i) == skip) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " skip handler %s",
+ mHandlers.get(i));
+ continue;
+ }
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
finishCB);
if (consumed) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 885f3dbed275..792f5cad3418 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -68,6 +68,8 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
/**
@@ -90,6 +92,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
private final Transitions mTransitions;
private final Region mExclusionRegion = Region.obtain();
private final InputManager mInputManager;
+ private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
private TaskOperations mTaskOperations;
private FocusTransitionObserver mFocusTransitionObserver;
@@ -130,7 +133,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
Transitions transitions,
- FocusTransitionObserver focusTransitionObserver) {
+ FocusTransitionObserver focusTransitionObserver,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -143,6 +147,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
mSyncQueue = syncQueue;
mTransitions = transitions;
mFocusTransitionObserver = focusTransitionObserver;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
}
@@ -332,7 +337,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
mMainHandler,
mBgExecutor,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mWindowDecorViewHostSupplier);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
final FluidResizeTaskPositioner taskPositioner =
@@ -462,7 +468,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
mDragPositioningCallback.onDragPositioningStart(
- 0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
+ 0 /* ctrlType */, e.getDisplayId(), e.getRawX(0), e.getRawY(0));
mIsDragging = false;
return false;
}
@@ -475,6 +481,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
if (decoration.isHandlingDragResize()) break;
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningMove(
+ e.getDisplayId(),
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mIsDragging = true;
return true;
@@ -486,6 +493,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
}
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
+ e.getDisplayId(),
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(newTaskBounds,
mWindowDecorByTaskId.get(mTaskId).calculateValidDragArea());
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 aa954fbe5669..23bb2aa616f9 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
@@ -58,6 +58,8 @@ 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.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
/**
@@ -90,8 +92,10 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
Handler handler,
@ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface);
+ SyncTransactionQueue syncQueue,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, windowDecorViewHostSupplier);
mHandler = handler;
mBgExecutor = bgExecutor;
mChoreographer = choreographer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
index 4d95cde1492f..01fc6440712d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
@@ -19,19 +19,18 @@ package com.android.wm.shell.windowdecor
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.graphics.Point
-import android.graphics.Rect
+import android.view.View
import android.view.WindowManager
import android.window.TaskSnapshot
import androidx.compose.ui.graphics.toArgb
+import com.android.wm.shell.R
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer
-import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
+import com.android.wm.shell.windowdecor.common.calculateMenuPosition
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
-import com.android.wm.shell.windowdecor.extension.isFullscreen
-import com.android.wm.shell.windowdecor.extension.isMultiWindow
/**
* Implementation of [ManageWindowsViewContainer] meant to be used in desktop header and app
@@ -59,35 +58,19 @@ class DesktopHandleManageWindowsMenu(
}
private fun calculateMenuPosition(): Point {
- val position = Point()
- val nonFreeformX = (captionX + (captionWidth / 2) - (menuView.menuWidth / 2))
- when {
- callerTaskInfo.isFreeform -> {
- val taskBounds = callerTaskInfo.getConfiguration().windowConfiguration.bounds
- position.set(taskBounds.left, taskBounds.top)
- }
- callerTaskInfo.isFullscreen -> {
- position.set(nonFreeformX, 0)
- }
- callerTaskInfo.isMultiWindow -> {
- val splitPosition = splitScreenController.getSplitPosition(callerTaskInfo.taskId)
- val leftOrTopStageBounds = Rect()
- val rightOrBottomStageBounds = Rect()
- splitScreenController.getStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
- when (splitPosition) {
- SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
- position.set(leftOrTopStageBounds.width() + nonFreeformX, /* y= */ 0)
- }
-
- SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
- position.set(nonFreeformX, /* y= */ 0)
- }
- }
- }
- }
- return position
+ return calculateMenuPosition(
+ splitScreenController,
+ callerTaskInfo,
+ marginStart = 0,
+ marginTop = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_margin_top
+ ),
+ captionX,
+ 0,
+ captionWidth,
+ menuView.menuWidth,
+ context.isRtl()
+ )
}
override fun addToContainer(menuView: ManageWindowsView) {
@@ -109,4 +92,7 @@ class DesktopHandleManageWindowsMenu(
override fun removeFromContainer() {
menuViewContainer?.releaseView()
}
+
+ private fun Context.isRtl() =
+ resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
}
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 e8b02dcb7a71..7928e5ed4188 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
@@ -79,6 +79,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
@@ -139,6 +140,9 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -147,7 +151,9 @@ import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
+import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.MainCoroutineDispatcher;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -174,6 +180,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final ShellController mShellController;
private final Context mContext;
private final @ShellMainThread Handler mMainHandler;
+ private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
+ private final @ShellBackgroundThread CoroutineScope mBgScope;
private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
@@ -217,6 +225,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private boolean mInImmersiveMode;
private final String mSysUIPackageName;
private final AssistContentRequester mAssistContentRequester;
+ private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
private final ISystemGestureExclusionListener mGestureExclusionListener =
@@ -237,12 +246,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final FocusTransitionObserver mFocusTransitionObserver;
private final DesktopModeEventLogger mDesktopModeEventLogger;
private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
+ private final WindowDecorTaskResourceLoader mTaskResourceLoader;
public DesktopModeWindowDecorViewModel(
Context context,
ShellExecutor shellExecutor,
@ShellMainThread Handler mainHandler,
Choreographer mainChoreographer,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -260,6 +272,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
AppHandleEducationController appHandleEducationController,
@@ -268,12 +281,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
- DesktopModeUiEventLogger desktopModeUiEventLogger) {
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
+ WindowDecorTaskResourceLoader taskResourceLoader) {
this(
context,
shellExecutor,
mainHandler,
mainChoreographer,
+ mainDispatcher,
+ bgScope,
bgExecutor,
shellInit,
shellCommandHandler,
@@ -289,6 +305,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
desktopImmersiveController,
genericLinksParser,
assistContentRequester,
+ windowDecorViewHostSupplier,
multiInstanceHelper,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
@@ -305,7 +322,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new TaskPositionerFactory(),
focusTransitionObserver,
desktopModeEventLogger,
- desktopModeUiEventLogger);
+ desktopModeUiEventLogger,
+ taskResourceLoader);
}
@VisibleForTesting
@@ -314,6 +332,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
ShellExecutor shellExecutor,
@ShellMainThread Handler mainHandler,
Choreographer mainChoreographer,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -329,6 +349,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
DesktopImmersiveController desktopImmersiveController,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
@@ -345,11 +366,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
TaskPositionerFactory taskPositionerFactory,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
- DesktopModeUiEventLogger desktopModeUiEventLogger) {
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
+ WindowDecorTaskResourceLoader taskResourceLoader) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
+ mMainDispatcher = mainDispatcher;
+ mBgScope = bgScope;
mBgExecutor = bgExecutor;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
@@ -381,6 +405,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
mAssistContentRequester = assistContentRequester;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -410,6 +435,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mFocusTransitionObserver = focusTransitionObserver;
mDesktopModeEventLogger = desktopModeEventLogger;
mDesktopModeUiEventLogger = desktopModeUiEventLogger;
+ mTaskResourceLoader = taskResourceLoader;
shellInit.addInitCallback(this::onInit, this);
}
@@ -580,8 +606,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private void openHandleMenu(int taskId) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
- decoration.createHandleMenu(checkNumberOfOtherInstances(decoration.mTaskInfo)
- >= MANAGE_WINDOWS_MINIMUM_INSTANCES);
+ // TODO(b/379873022): Run the instance check and the AssistContent request in
+ // createHandleMenu on the same bg thread dispatch.
+ mBgExecutor.execute(() -> {
+ final int numOfInstances = checkNumberOfOtherInstances(decoration.mTaskInfo);
+ mMainExecutor.execute(() -> {
+ decoration.createHandleMenu(
+ numOfInstances >= MANAGE_WINDOWS_MINIMUM_INSTANCES
+ );
+ });
+ });
}
private void onToggleSizeInteraction(
@@ -701,7 +735,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// App sometimes draws before the insets from WindowDecoration#relayout have
// been added, so they must be added here
decoration.addCaptionInset(wct);
- mDesktopTasksController.moveTaskToDesktop(taskId, wct, source);
+ mDesktopTasksController.moveTaskToDesktop(taskId, wct, source,
+ /* remoteTransition= */ null);
decoration.closeHandleMenu();
if (source == DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) {
@@ -755,12 +790,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return;
}
decoration.closeHandleMenu();
- decoration.createManageWindowsMenu(getTaskSnapshots(decoration.mTaskInfo),
- /* onIconClickListener= */(Integer requestedTaskId) -> {
- decoration.closeManageWindowsMenu();
- mDesktopTasksController.openInstance(decoration.mTaskInfo, requestedTaskId);
- return Unit.INSTANCE;
- });
+ mBgExecutor.execute(() -> {
+ final ArrayList<Pair<Integer, TaskSnapshot>> snapshotList =
+ getTaskSnapshots(decoration.mTaskInfo);
+ mMainExecutor.execute(() -> decoration.createManageWindowsMenu(
+ snapshotList,
+ /* onIconClickListener= */ (Integer requestedTaskId) -> {
+ decoration.closeManageWindowsMenu();
+ mDesktopTasksController.openInstance(decoration.mTaskInfo,
+ requestedTaskId);
+ return Unit.INSTANCE;
+ }
+ )
+ );
+ });
}
private ArrayList<Pair<Integer, TaskSnapshot>> getTaskSnapshots(
@@ -965,8 +1008,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
if (mInputManager != null
&& !Flags.enableAccessibleCustomHeaders()) {
- // Pilfer so that windows below receive cancellations for this gesture.
- mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
+ ViewRootImpl viewRootImpl = v.getViewRootImpl();
+ if (viewRootImpl != null) {
+ // Pilfer so that windows below receive cancellations for this gesture.
+ mInputManager.pilferPointers(viewRootImpl.getInputToken());
+ }
}
if (isUpOrCancel) {
// Gesture is finished, reset state.
@@ -1094,7 +1140,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
if (dragAllowed) {
mDragPointerId = e.getPointerId(0);
final Rect initialBounds = mDragPositioningCallback.onDragPositioningStart(
- 0 /* ctrlType */, e.getRawX(0),
+ 0 /* ctrlType */, e.getDisplayId(), e.getRawX(0),
e.getRawY(0));
updateDragStatus(e.getActionMasked());
mOnDragStartInitialBounds.set(initialBounds);
@@ -1115,6 +1161,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
+ e.getDisplayId(),
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.onDragPositioningMove(taskInfo,
decoration.mTaskSurface,
@@ -1145,6 +1192,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
(int) (e.getRawX(dragPointerIdx) - e.getX(dragPointerIdx)),
(int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
+ e.getDisplayId(),
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
// Tasks bounds haven't actually been updated (only its leash), so pass to
// DesktopTasksController to allow secondary transformations (i.e. snap resizing
@@ -1607,15 +1655,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
final DesktopModeWindowDecoration windowDecoration =
mDesktopModeWindowDecorFactory.create(
- mContext,
+ Flags.enableBugFixesForSecondaryDisplay()
+ ? mDisplayController.getDisplayContext(taskInfo.displayId)
+ : mContext,
mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
mDisplayController,
+ mTaskResourceLoader,
mSplitScreenController,
mDesktopUserRepositories,
mTaskOrganizer,
taskInfo,
taskSurface,
mMainHandler,
+ mMainExecutor,
+ mMainDispatcher,
+ mBgScope,
mBgExecutor,
mMainChoreographer,
mSyncQueue,
@@ -1623,6 +1677,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
mAssistContentRequester,
+ mWindowDecorViewHostSupplier,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
mDesktopModeEventLogger);
@@ -1802,11 +1857,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// TODO(b/336289597): Rather than returning number of instances, return a list of valid
// instances, then refer to the list's size and reuse the list for Manage Windows menu.
final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
- final IActivityManager activityManager = ActivityManager.getService();
try {
return activityTaskManager.getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_WITH_EXCLUDED,
- activityManager.getCurrentUserId()).getList().stream().filter(
+ info.userId).getList().stream().filter(
recentTaskInfo -> (recentTaskInfo.taskId != info.taskId
&& recentTaskInfo.baseActivity != null
&& recentTaskInfo.baseActivity.getPackageName()
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 e7985dea61c8..0d1960ad6e29 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
@@ -28,7 +28,6 @@ import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
-import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopModeOrShowAppHandle;
import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
@@ -43,16 +42,12 @@ import static com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.Dr
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.app.assist.AssistContent;
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.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -61,13 +56,11 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Size;
-import android.util.Slog;
import android.view.Choreographer;
import android.view.InsetsState;
import android.view.MotionEvent;
@@ -82,8 +75,6 @@ import android.window.TaskSnapshot;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.IconProvider;
import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -99,13 +90,18 @@ 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.DesktopModeUtils;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -116,7 +112,11 @@ import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -128,17 +128,20 @@ import java.util.function.Supplier;
*/
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
- private static final int CAPTURED_LINK_TIMEOUT_MS = 7000;
@VisibleForTesting
static final long CLOSE_MAXIMIZE_MENU_DELAY_MS = 150L;
- private final Handler mHandler;
+ private final @ShellMainThread Handler mHandler;
+ private final @ShellMainThread ShellExecutor mMainExecutor;
+ private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
+ private final @ShellBackgroundThread CoroutineScope mBgScope;
private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
private final SplitScreenController mSplitScreenController;
private final WindowManagerWrapper mWindowManagerWrapper;
+ private final @NonNull WindowDecorTaskResourceLoader mTaskResourceLoader;
private WindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
@@ -158,14 +161,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Function0<Unit> mOnMaximizeHoverListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
- private Runnable mCurrentViewHostRunnable = null;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private DisabledEdge mDisabledResizingEdge =
NONE;
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
- private final Runnable mViewHostRunnable =
- () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult);
private final Point mPositionInParent = new Point();
private HandleMenu mHandleMenu;
@@ -177,10 +177,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private OpenByDefaultDialog mOpenByDefaultDialog;
private ResizeVeil mResizeVeil;
- private Bitmap mAppIconBitmap;
- private Bitmap mResizeVeilBitmap;
- private CharSequence mAppName;
private CapturedLink mCapturedLink;
private Uri mGenericLink;
private Uri mWebUri;
@@ -203,21 +200,27 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// being hovered. There's a small delay after stopping the hover, to allow a quick reentry
// to cancel the close.
private final Runnable mCloseMaximizeWindowRunnable = this::closeMaximizeMenu;
- private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired;
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
+ private Runnable mLoadAppInfoRunnable;
+ private Runnable mSetAppInfoRunnable;
+
public DesktopModeWindowDecoration(
Context context,
@NonNull Context userContext,
DisplayController displayController,
+ @NonNull WindowDecorTaskResourceLoader taskResourceLoader,
SplitScreenController splitScreenController,
DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Handler handler,
+ @ShellMainThread Handler handler,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
@@ -225,17 +228,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
- this (context, userContext, displayController, splitScreenController,
+ this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
- bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory,
- rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
- SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
- WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
+ mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
+ appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
+ assistContentRequester, SurfaceControl.Builder::new,
+ SurfaceControl.Transaction::new, WindowContainerTransaction::new,
+ SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
new SurfaceControlViewHostFactory() {},
+ windowDecorViewHostSupplier,
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
windowDecorCaptionHandleRepository, desktopModeEventLogger);
@@ -245,12 +251,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Context context,
@NonNull Context userContext,
DisplayController displayController,
+ @NonNull WindowDecorTaskResourceLoader taskResourceLoader,
SplitScreenController splitScreenController,
DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Handler handler,
+ @ShellMainThread Handler handler,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
@@ -264,17 +274,21 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Supplier<SurfaceControl> surfaceControlSupplier,
WindowManagerWrapper windowManagerWrapper,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MaximizeMenuFactory maximizeMenuFactory,
HandleMenuFactory handleMenuFactory,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
- surfaceControlViewHostFactory, desktopModeEventLogger);
+ surfaceControlViewHostFactory, windowDecorViewHostSupplier, desktopModeEventLogger);
mSplitScreenController = splitScreenController;
mHandler = handler;
+ mMainExecutor = mainExecutor;
+ mMainDispatcher = mainDispatcher;
+ mBgScope = bgScope;
mBgExecutor = bgExecutor;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
@@ -288,6 +302,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mWindowManagerWrapper = windowManagerWrapper;
mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
mDesktopUserRepositories = desktopUserRepositories;
+ mTaskResourceLoader = taskResourceLoader;
+ mTaskResourceLoader.onWindowDecorCreated(taskInfo);
}
/**
@@ -451,78 +467,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
- if (taskInfo.isFreeform()) {
- // The Task is in Freeform mode -> show its header in sync since it's an integral part
- // of the window itself - a delayed header might cause bad UX.
- relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- } else {
- // The Task is outside Freeform mode -> allow the handle view to be delayed since the
- // handle is just a small addition to the window.
- relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- }
- Trace.endSection();
- }
-
- /** Run the whole relayout phase immediately without delay. */
- private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
- // Clear the current ViewHost runnable as we will update the ViewHost here
- clearCurrentViewHostRunnable();
- updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- if (mResult.mRootView != null) {
- updateViewHost(mRelayoutParams, startT, mResult);
- }
- }
- /**
- * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been
- * updated.
- */
- private void clearCurrentViewHostRunnable() {
- if (mCurrentViewHostRunnable != null) {
- mHandler.removeCallbacks(mCurrentViewHostRunnable);
- mCurrentViewHostRunnable = null;
- }
- }
-
- /**
- * Relayout the window decoration but repost some of the work, to unblock the current callstack.
- */
- private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus,
- @NonNull Region displayExclusionRegion) {
- if (applyStartTransactionOnDraw) {
- throw new IllegalArgumentException(
- "We cannot both sync viewhost ondraw and delay viewhost creation.");
- }
- // Clear the current ViewHost runnable as we will update the ViewHost here
- clearCurrentViewHostRunnable();
- updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
- false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion);
- if (mResult.mRootView == null) {
- // This means something blocks the window decor from showing, e.g. the task is hidden.
- // Nothing is set up in this case including the decoration surface.
- return;
- }
- // Store the current runnable so it can be removed if we start a new relayout.
- mCurrentViewHostRunnable = mViewHostRunnable;
- mHandler.post(mCurrentViewHostRunnable);
- }
-
- @SuppressLint("MissingPermission")
- private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
- Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
if (Flags.enableDesktopWindowingAppToWeb()) {
setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
}
@@ -553,9 +498,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces");
- updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
- Trace.endSection();
+ relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
@@ -570,15 +513,22 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeStatusBarInputLayer();
- Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
if (oldRootView != mResult.mRootView) {
disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
+ // Load these only when first creating the view.
+ loadTaskNameAndIconInBackground((name, icon) -> {
+ final AppHeaderViewHolder appHeader = asAppHeader(mWindowDecorViewHolder);
+ if (appHeader != null) {
+ appHeader.setAppName(name);
+ appHeader.setAppIcon(icon);
+ }
+ });
}
- Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
final Point position = new Point();
if (isAppHandle(mWindowDecorViewHolder)) {
@@ -588,6 +538,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
notifyCaptionStateChanged();
}
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
if (isAppHandle(mWindowDecorViewHolder)) {
mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
@@ -596,7 +547,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
} else {
mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
- TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
inFullImmersive,
hasGlobalFocus,
/* maximizeHoverEnabled= */ canOpenMaximizeMenu(
@@ -612,7 +563,34 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
updateDragResizeListener(oldDecorationSurface, inFullImmersive);
updateMaximizeMenu(startT, inFullImmersive);
- Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ }
+
+ /**
+ * Loads the task's name and icon in a background thread and posts the results back in the
+ * main thread.
+ */
+ private void loadTaskNameAndIconInBackground(BiConsumer<CharSequence, Bitmap> onResult) {
+ if (mWindowDecorViewHolder == null) return;
+ if (asAppHeader(mWindowDecorViewHolder) == null) {
+ // Only needed when drawing a header.
+ return;
+ }
+ if (mLoadAppInfoRunnable != null) {
+ mBgExecutor.removeCallbacks(mLoadAppInfoRunnable);
+ }
+ if (mSetAppInfoRunnable != null) {
+ mMainExecutor.removeCallbacks(mSetAppInfoRunnable);
+ }
+ mLoadAppInfoRunnable = () -> {
+ final CharSequence name = mTaskResourceLoader.getName(mTaskInfo);
+ final Bitmap icon = mTaskResourceLoader.getHeaderIcon(mTaskInfo);
+ mSetAppInfoRunnable = () -> {
+ onResult.accept(name, icon);
+ };
+ mMainExecutor.execute(mSetAppInfoRunnable);
+ };
+ mBgExecutor.execute(mLoadAppInfoRunnable);
}
private boolean isCaptionVisible() {
@@ -625,38 +603,30 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return;
}
mCapturedLink = new CapturedLink(capturedLink, timeStamp);
- mHandler.postDelayed(mCapturedLinkExpiredRunnable, CAPTURED_LINK_TIMEOUT_MS);
- }
-
- private void onCapturedLinkExpired() {
- mHandler.removeCallbacks(mCapturedLinkExpiredRunnable);
- if (mCapturedLink != null) {
- mCapturedLink.setExpired();
- }
}
@Nullable
private Intent getBrowserLink() {
final Uri browserLink;
- // If the captured link is available and has not expired, return the captured link.
- // Otherwise, return the generic link which is set to null if a generic link is unavailable.
- if (mCapturedLink != null && !mCapturedLink.mExpired) {
- browserLink = mCapturedLink.mUri;
- } else if (mWebUri != null) {
+ if (mWebUri != null) {
browserLink = mWebUri;
+ } else if (isCapturedLinkAvailable()) {
+ browserLink = mCapturedLink.mUri;
} else {
browserLink = mGenericLink;
}
if (browserLink == null) return null;
- return AppToWebUtils.getBrowserIntent(browserLink, mContext.getPackageManager());
+ return AppToWebUtils.getBrowserIntent(browserLink, mContext.getPackageManager(),
+ mUserContext.getUserId());
}
@Nullable
private Intent getAppLink() {
return mWebUri == null ? null
- : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager());
+ : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager(),
+ mUserContext.getUserId());
}
private boolean isBrowserApp() {
@@ -752,7 +722,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
private boolean isCapturedLinkAvailable() {
- return mCapturedLink != null && !mCapturedLink.mExpired;
+ return mCapturedLink != null && !mCapturedLink.mUsed;
+ }
+
+ private void onCapturedLinkUsed() {
+ if (mCapturedLink != null) {
+ mCapturedLink.setUsed();
+ }
}
private void notifyNoCaptionHandle() {
@@ -805,12 +781,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final Point position = new Point(mResult.mCaptionX, 0);
if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
== SPLIT_POSITION_BOTTOM_OR_RIGHT
- && mDisplayController.getDisplayLayout(mTaskInfo.displayId).isLandscape()
) {
- // If this is the right split task, add left stage's width.
- final Rect leftStageBounds = new Rect();
- mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
- position.x += leftStageBounds.width();
+ if (mSplitScreenController.isLeftRightSplit()) {
+ // If this is the right split task, add left stage's width.
+ final Rect leftStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+ position.x += leftStageBounds.width();
+ } else {
+ final Rect bottomStageBounds = new Rect();
+ mSplitScreenController.getRefStageBounds(new Rect(), bottomStageBounds);
+ position.y += bottomStageBounds.top;
+ }
}
return position;
}
@@ -838,15 +819,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
);
} else if (mRelayoutParams.mLayoutResId
== R.layout.desktop_mode_app_header) {
- loadAppInfoIfNeeded();
return mAppHeaderViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mOnCaptionLongClickListener,
mOnCaptionGenericMotionListener,
- mAppName,
- mAppIconBitmap,
mOnMaximizeHoverListener);
}
throw new IllegalArgumentException("Unexpected layout resource id");
@@ -897,6 +875,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
relayoutParams.mHasGlobalFocus = hasGlobalFocus;
relayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
+ // Allow the handle view to be delayed since the handle is just a small addition to the
+ // window, whereas the header cannot be delayed because it is expected to be visible from
+ // the first frame.
+ relayoutParams.mAsyncViewHost = isAppHandle;
final boolean showCaption;
if (Flags.enableFullyImmersiveInDesktop()) {
@@ -1106,7 +1088,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskInfo,
mTaskSurface,
mDisplayController,
+ mTaskResourceLoader,
mSurfaceControlTransactionSupplier,
+ mMainDispatcher,
+ mBgScope,
new OpenByDefaultDialog.DialogLifecycleListener() {
@Override
public void onDialogCreated() {
@@ -1117,9 +1102,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
public void onDialogDismissed() {
mOpenByDefaultDialog = null;
}
- },
- mAppIconBitmap,
- mAppName
+ }
);
}
@@ -1131,50 +1114,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize();
}
- private void loadAppInfoIfNeeded() {
- // TODO(b/337370277): move this to another thread.
- try {
- Trace.beginSection("DesktopModeWindowDecoration#loadAppInfoIfNeeded");
- if (mAppIconBitmap != null && mAppName != null) {
- return;
- }
- if (mTaskInfo.baseIntent == null) {
- Slog.e(TAG, "Base intent not found in task");
- return;
- }
- final PackageManager pm = mUserContext.getPackageManager();
- final ActivityInfo activityInfo =
- pm.getActivityInfo(mTaskInfo.baseIntent.getComponent(), 0 /* flags */);
- final IconProvider provider = new IconProvider(mContext);
- final Drawable appIconDrawable = provider.getIcon(activityInfo);
- final Drawable badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable,
- UserHandle.of(mTaskInfo.userId));
- final BaseIconFactory headerIconFactory = createIconFactory(mContext,
- R.dimen.desktop_mode_caption_icon_radius);
- mAppIconBitmap = headerIconFactory.createIconBitmap(badgedAppIconDrawable,
- 1f /* scale */);
-
- final BaseIconFactory resizeVeilIconFactory = createIconFactory(mContext,
- R.dimen.desktop_mode_resize_veil_icon_size);
- mResizeVeilBitmap = resizeVeilIconFactory
- .createScaledBitmap(appIconDrawable, MODE_DEFAULT);
-
- final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
- mAppName = pm.getApplicationLabel(applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Base activity's component name cannot be found on the system", e);
- } finally {
- Trace.endSection();
- }
- }
-
- private BaseIconFactory createIconFactory(Context context, int dimensions) {
- final Resources resources = context.getResources();
- final int densityDpi = resources.getDisplayMetrics().densityDpi;
- final int iconSize = resources.getDimensionPixelSize(dimensions);
- return new BaseIconFactory(context, densityDpi, iconSize);
- }
-
private void closeDragResizeListener() {
if (mDragResizeListener == null) {
return;
@@ -1189,9 +1128,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
private void createResizeVeilIfNeeded() {
if (mResizeVeil != null) return;
- loadAppInfoIfNeeded();
- mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap,
- mTaskSurface, mSurfaceControlTransactionSupplier, mTaskInfo);
+ mResizeVeil = new ResizeVeil(mContext, mDisplayController, mTaskResourceLoader,
+ mMainDispatcher, mBgScope, mTaskSurface,
+ mSurfaceControlTransactionSupplier, mTaskInfo);
}
/**
@@ -1390,8 +1329,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
@VisibleForTesting
void onAssistContentReceived(@Nullable AssistContent assistContent) {
- mWebUri = assistContent == null ? null : assistContent.getWebUri();
- loadAppInfoIfNeeded();
+ mWebUri = assistContent == null ? null : AppToWebUtils.getSessionWebUri(assistContent);
updateGenericLink();
final boolean supportsMultiInstance = mMultiInstanceHelper
.supportsMultiInstanceSplit(mTaskInfo.baseActivity)
@@ -1404,11 +1342,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
.isTaskInFullImmersiveState(mTaskInfo.taskId);
final boolean isBrowserApp = isBrowserApp();
mHandleMenu = mHandleMenuFactory.create(
+ mMainDispatcher,
+ mBgScope,
this,
mWindowManagerWrapper,
+ mTaskResourceLoader,
mRelayoutParams.mLayoutResId,
- mAppIconBitmap,
- mAppName,
mSplitScreenController,
canEnterDesktopModeOrShowAppHandle(mContext),
supportsMultiInstance,
@@ -1438,7 +1377,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
/* onAspectRatioSettingsClickListener= */ mOnChangeAspectRatioClickListener,
/* openInBrowserClickListener= */ (intent) -> {
mOpenInBrowserClickListener.accept(intent);
- onCapturedLinkExpired();
+ onCapturedLinkUsed();
if (Flags.enableDesktopWindowingAppToWebEducationIntegration()) {
mWindowDecorCaptionHandleRepository.onAppToWebUsage();
}
@@ -1697,13 +1636,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@Override
public void close() {
+ if (mLoadAppInfoRunnable != null) {
+ mBgExecutor.removeCallbacks(mLoadAppInfoRunnable);
+ }
+ if (mSetAppInfoRunnable != null) {
+ mMainExecutor.removeCallbacks(mSetAppInfoRunnable);
+ }
+ mTaskResourceLoader.onWindowDecorClosed(mTaskInfo);
closeDragResizeListener();
closeHandleMenu();
closeManageWindowsMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
disposeStatusBarInputLayer();
- clearCurrentViewHostRunnable();
+ mWindowDecorViewHolder = null;
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyNoCaptionHandle();
}
@@ -1780,7 +1726,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
.isTaskInFullImmersiveState(mTaskInfo.taskId);
asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
- TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
inFullImmersive,
isFocused(),
/* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition)));
@@ -1827,12 +1773,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Context context,
@NonNull Context userContext,
DisplayController displayController,
+ @NonNull WindowDecorTaskResourceLoader appResourceProvider,
SplitScreenController splitScreenController,
DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Handler handler,
+ @ShellMainThread Handler handler,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
@@ -1840,6 +1790,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost>
+ windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
@@ -1847,12 +1799,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
context,
userContext,
displayController,
+ appResourceProvider,
splitScreenController,
desktopUserRepositories,
taskOrganizer,
taskInfo,
taskSurface,
handler,
+ mainExecutor,
+ mainDispatcher,
+ bgScope,
bgExecutor,
choreographer,
syncQueue,
@@ -1860,6 +1816,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
rootTaskDisplayAreaOrganizer,
genericLinksParser,
assistContentRequester,
+ windowDecorViewHostSupplier,
multiInstanceHelper,
windowDecorCaptionHandleRepository,
desktopModeEventLogger);
@@ -1870,16 +1827,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
static class CapturedLink {
private final long mTimeStamp;
private final Uri mUri;
- private boolean mExpired;
+ private boolean mUsed;
CapturedLink(@NonNull Uri uri, long timeStamp) {
mUri = uri;
mTimeStamp = timeStamp;
- mExpired = false;
}
- void setExpired() {
- mExpired = true;
+ private void setUsed() {
+ mUsed = true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index 421ffd929fb2..3eebdb048f0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -41,25 +41,30 @@ public interface DragPositioningCallback {
*
* @param ctrlType {@link CtrlType} indicating the direction of resizing, use
* {@code 0} to indicate it's a move
+ * @param displayId the ID of the display where the drag starts
* @param x x coordinate in window decoration coordinate system where the drag starts
* @param y y coordinate in window decoration coordinate system where the drag starts
* @return the starting task bounds
*/
- Rect onDragPositioningStart(@CtrlType int ctrlType, float x, float y);
+ Rect onDragPositioningStart(@CtrlType int ctrlType, int displayId, float x, float y);
/**
* Called when the pointer moves during a drag-resize or drag-move.
+ *
+ * @param displayId the ID of the display where the pointer is currently located
* @param x x coordinate in window decoration coordinate system of the new pointer location
* @param y y coordinate in window decoration coordinate system of the new pointer location
* @return the updated task bounds
*/
- Rect onDragPositioningMove(float x, float y);
+ Rect onDragPositioningMove(int displayId, float x, float y);
/**
* Called when a drag-resize or drag-move stops.
+ *
+ * @param displayId the ID of the display where the pointer is located when drag stops
* @param x x coordinate in window decoration coordinate system where the drag resize stops
* @param y y coordinate in window decoration coordinate system where the drag resize stops
* @return the final bounds for the dragged task
*/
- Rect onDragPositioningEnd(float x, float y);
+ Rect onDragPositioningEnd(int displayId, float x, float y);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index a6d503d0d991..7d1471f44674 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -454,7 +454,7 @@ class DragResizeInputListener implements AutoCloseable {
ProtoLog.d(WM_SHELL_DESKTOP_MODE,
"%s: Handling action down, update ctrlType to %d", TAG, ctrlType);
mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
- rawX, rawY);
+ e.getDisplayId(), rawX, rawY);
mLastMotionEventOnDown = e;
mResizeTrigger = (ctrlType == CTRL_TYPE_BOTTOM || ctrlType == CTRL_TYPE_TOP
|| ctrlType == CTRL_TYPE_RIGHT || ctrlType == CTRL_TYPE_LEFT)
@@ -489,7 +489,8 @@ class DragResizeInputListener implements AutoCloseable {
}
final float rawX = e.getRawX(dragPointerIndex);
final float rawY = e.getRawY(dragPointerIndex);
- final Rect taskBounds = mCallback.onDragPositioningMove(rawX, rawY);
+ final Rect taskBounds = mCallback.onDragPositioningMove(e.getDisplayId(),
+ rawX, rawY);
updateInputSinkRegionForDrag(taskBounds);
result = true;
break;
@@ -505,7 +506,7 @@ class DragResizeInputListener implements AutoCloseable {
TAG, e.getActionMasked());
break;
}
- final Rect taskBounds = mCallback.onDragPositioningEnd(
+ final Rect taskBounds = mCallback.onDragPositioningEnd(e.getDisplayId(),
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
// If taskBounds has changed, setGeometry will be called and update the
// sink region. Otherwise, we should revert it here.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index c8aff78cbb36..5b027f3c039e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -168,7 +168,10 @@ public final class DragResizeWindowGeometry {
return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
}
- static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
+ /**
+ * Whether resizing a window from the edge is permitted based on the motion event.
+ */
+ public static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
|| e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
index 3885761d0742..ab30d617af54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
@@ -47,10 +47,11 @@ class FixedAspectRatioTaskPositionerDecorator (
private var startingAspectRatio = 0f
private var isTaskPortrait = false
- override fun onDragPositioningStart(@CtrlType ctrlType: Int, x: Float, y: Float): Rect {
+ override fun onDragPositioningStart(
+ @CtrlType ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
originalCtrlType = ctrlType
if (!requiresFixedAspectRatio()) {
- return super.onDragPositioningStart(originalCtrlType, x, y)
+ return super.onDragPositioningStart(originalCtrlType, displayId, x, y)
}
lastRepositionedBounds.set(getBounds(windowDecoration.mTaskInfo))
@@ -72,27 +73,27 @@ class FixedAspectRatioTaskPositionerDecorator (
val verticalMidPoint = lastRepositionedBounds.top + (startingBoundHeight / 2)
edgeResizeCtrlType = originalCtrlType +
if (y < verticalMidPoint) CTRL_TYPE_TOP else CTRL_TYPE_BOTTOM
- super.onDragPositioningStart(edgeResizeCtrlType, x, y)
+ super.onDragPositioningStart(edgeResizeCtrlType, displayId, x, y)
}
CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
val horizontalMidPoint = lastRepositionedBounds.left + (startingBoundWidth / 2)
edgeResizeCtrlType = originalCtrlType +
if (x < horizontalMidPoint) CTRL_TYPE_LEFT else CTRL_TYPE_RIGHT
- super.onDragPositioningStart(edgeResizeCtrlType, x, y)
+ super.onDragPositioningStart(edgeResizeCtrlType, displayId, x, y)
}
// If resize is corner resize, no alteration to the ctrlType needs to be made.
else -> {
edgeResizeCtrlType = CTRL_TYPE_UNDEFINED
- super.onDragPositioningStart(originalCtrlType, x, y)
+ super.onDragPositioningStart(originalCtrlType, displayId, x, y)
}
}
)
return lastRepositionedBounds
}
- override fun onDragPositioningMove(x: Float, y: Float): Rect {
+ override fun onDragPositioningMove(displayId: Int, x: Float, y: Float): Rect {
if (!requiresFixedAspectRatio()) {
- return super.onDragPositioningMove(x, y)
+ return super.onDragPositioningMove(displayId, x, y)
}
val diffX = x - lastValidPoint.x
@@ -103,7 +104,7 @@ class FixedAspectRatioTaskPositionerDecorator (
// Drag coordinate falls within valid region (90 - 180 degrees or 270- 360
// degrees from the corner the previous valid point). Allow resize with adjusted
// coordinates to maintain aspect ratio.
- lastRepositionedBounds.set(dragAdjustedMove(x, y))
+ lastRepositionedBounds.set(dragAdjustedMove(displayId, x, y))
}
}
CTRL_TYPE_BOTTOM + CTRL_TYPE_LEFT, CTRL_TYPE_TOP + CTRL_TYPE_RIGHT -> {
@@ -111,28 +112,28 @@ class FixedAspectRatioTaskPositionerDecorator (
// Drag coordinate falls within valid region (180 - 270 degrees or 0 - 90
// degrees from the corner the previous valid point). Allow resize with adjusted
// coordinates to maintain aspect ratio.
- lastRepositionedBounds.set(dragAdjustedMove(x, y))
+ lastRepositionedBounds.set(dragAdjustedMove(displayId, x, y))
}
}
CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT -> {
// If resize is on left or right edge, always adjust the y coordinate.
val adjustedY = getScaledChangeForY(x)
lastValidPoint.set(x, adjustedY)
- lastRepositionedBounds.set(super.onDragPositioningMove(x, adjustedY))
+ lastRepositionedBounds.set(super.onDragPositioningMove(displayId, x, adjustedY))
}
CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
// If resize is on top or bottom edge, always adjust the x coordinate.
val adjustedX = getScaledChangeForX(y)
lastValidPoint.set(adjustedX, y)
- lastRepositionedBounds.set(super.onDragPositioningMove(adjustedX, y))
+ lastRepositionedBounds.set(super.onDragPositioningMove(displayId, adjustedX, y))
}
}
return lastRepositionedBounds
}
- override fun onDragPositioningEnd(x: Float, y: Float): Rect {
+ override fun onDragPositioningEnd(displayId: Int, x: Float, y: Float): Rect {
if (!requiresFixedAspectRatio()) {
- return super.onDragPositioningEnd(x, y)
+ return super.onDragPositioningEnd(displayId, x, y)
}
val diffX = x - lastValidPoint.x
@@ -144,55 +145,55 @@ class FixedAspectRatioTaskPositionerDecorator (
// Drag coordinate falls within valid region (90 - 180 degrees or 270- 360
// degrees from the corner the previous valid point). End resize with adjusted
// coordinates to maintain aspect ratio.
- return dragAdjustedEnd(x, y)
+ return dragAdjustedEnd(displayId, x, y)
}
// If end of resize is not within valid region, end resize from last valid
// coordinates.
- return super.onDragPositioningEnd(lastValidPoint.x, lastValidPoint.y)
+ return super.onDragPositioningEnd(displayId, lastValidPoint.x, lastValidPoint.y)
}
CTRL_TYPE_BOTTOM + CTRL_TYPE_LEFT, CTRL_TYPE_TOP + CTRL_TYPE_RIGHT -> {
if ((diffX > 0 && diffY < 0) || (diffX < 0 && diffY > 0)) {
// Drag coordinate falls within valid region (180 - 260 degrees or 0 - 90
// degrees from the corner the previous valid point). End resize with adjusted
// coordinates to maintain aspect ratio.
- return dragAdjustedEnd(x, y)
+ return dragAdjustedEnd(displayId, x, y)
}
// If end of resize is not within valid region, end resize from last valid
// coordinates.
- return super.onDragPositioningEnd(lastValidPoint.x, lastValidPoint.y)
+ return super.onDragPositioningEnd(displayId, lastValidPoint.x, lastValidPoint.y)
}
CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT -> {
// If resize is on left or right edge, always adjust the y coordinate.
- return super.onDragPositioningEnd(x, getScaledChangeForY(x))
+ return super.onDragPositioningEnd(displayId, x, getScaledChangeForY(x))
}
CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
// If resize is on top or bottom edge, always adjust the x coordinate.
- return super.onDragPositioningEnd(getScaledChangeForX(y), y)
+ return super.onDragPositioningEnd(displayId, getScaledChangeForX(y), y)
}
else -> {
- return super.onDragPositioningEnd(x, y)
+ return super.onDragPositioningEnd(displayId, x, y)
}
}
}
- private fun dragAdjustedMove(x: Float, y: Float): Rect {
+ private fun dragAdjustedMove(displayId: Int, x: Float, y: Float): Rect {
val absDiffX = abs(x - lastValidPoint.x)
val absDiffY = abs(y - lastValidPoint.y)
if (absDiffY < absDiffX) {
lastValidPoint.set(getScaledChangeForX(y), y)
- return super.onDragPositioningMove(getScaledChangeForX(y), y)
+ return super.onDragPositioningMove(displayId, getScaledChangeForX(y), y)
}
lastValidPoint.set(x, getScaledChangeForY(x))
- return super.onDragPositioningMove(x, getScaledChangeForY(x))
+ return super.onDragPositioningMove(displayId, x, getScaledChangeForY(x))
}
- private fun dragAdjustedEnd(x: Float, y: Float): Rect {
+ private fun dragAdjustedEnd(displayId: Int, x: Float, y: Float): Rect {
val absDiffX = abs(x - lastValidPoint.x)
val absDiffY = abs(y - lastValidPoint.y)
if (absDiffY < absDiffX) {
- return super.onDragPositioningEnd(getScaledChangeForX(y), y)
+ return super.onDragPositioningEnd(displayId, getScaledChangeForX(y), y)
}
- return super.onDragPositioningEnd(x, getScaledChangeForY(x))
+ return super.onDragPositioningEnd(displayId, x, getScaledChangeForY(x))
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 3efae9d6375a..2d6f7459e0ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -91,7 +91,7 @@ class FluidResizeTaskPositioner implements TaskPositioner, Transitions.Transitio
}
@Override
- public Rect onDragPositioningStart(int ctrlType, float x, float y) {
+ public Rect onDragPositioningStart(int ctrlType, int displayId, float x, float y) {
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -117,7 +117,7 @@ class FluidResizeTaskPositioner implements TaskPositioner, Transitions.Transitio
}
@Override
- public Rect onDragPositioningMove(float x, float y) {
+ public Rect onDragPositioningMove(int displayId, float x, float y) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint);
if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
@@ -147,7 +147,7 @@ class FluidResizeTaskPositioner implements TaskPositioner, Transitions.Transitio
}
@Override
- public Rect onDragPositioningEnd(float x, float y) {
+ public Rect onDragPositioningEnd(int displayId, float x, float y) {
// If task has been resized or task was dragged into area outside of
// mDisallowedAreaForEndBounds, apply WCT to finish it.
if (isResizing() && mHasDragResized) {
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 049b8d621427..bb19a2cc2ad4 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
@@ -47,15 +47,25 @@ import androidx.compose.ui.graphics.toArgb
import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.apptoweb.isBrowserApp
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.calculateMenuPosition
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.extension.isFullscreen
import com.android.wm.shell.windowdecor.extension.isMultiWindow
import com.android.wm.shell.windowdecor.extension.isPinned
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
/**
* Handle menu opened when the appropriate button is clicked on.
@@ -66,11 +76,12 @@ import com.android.wm.shell.windowdecor.extension.isPinned
* Additional Options: Miscellaneous functions including screenshot and closing task.
*/
class HandleMenu(
+ @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
private val parentDecor: DesktopModeWindowDecoration,
private val windowManagerWrapper: WindowManagerWrapper,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
private val layoutResId: Int,
- private val appIconBitmap: Bitmap?,
- private val appName: CharSequence?,
private val splitScreenController: SplitScreenController,
private val shouldShowWindowingPill: Boolean,
private val shouldShowNewWindowButton: Boolean,
@@ -103,7 +114,8 @@ class HandleMenu(
@VisibleForTesting
var handleMenuViewContainer: AdditionalViewContainer? = null
- private var handleMenuView: HandleMenuView? = null
+ @VisibleForTesting
+ var handleMenuView: HandleMenuView? = null
// Position of the handle menu used for laying out the handle view.
@VisibleForTesting
@@ -122,6 +134,8 @@ class HandleMenu(
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ private var loadAppInfoJob: Job? = null
+
init {
updateHandleMenuPillPositions(captionX, captionY)
}
@@ -190,7 +204,7 @@ class HandleMenu(
shouldShowDesktopModeButton = shouldShowDesktopModeButton,
isBrowserApp = isBrowserApp
).apply {
- bind(taskInfo, appIconBitmap, appName, shouldShowMoreActionsPill)
+ bind(taskInfo, shouldShowMoreActionsPill)
this.onToDesktopClickListener = onToDesktopClickListener
this.onToFullscreenClickListener = onToFullscreenClickListener
this.onToSplitScreenClickListener = onToSplitScreenClickListener
@@ -204,7 +218,16 @@ class HandleMenu(
this.onCloseMenuClickListener = onCloseMenuClickListener
this.onOutsideTouchListener = onOutsideTouchListener
}
-
+ loadAppInfoJob = bgScope.launch {
+ if (!isActive) return@launch
+ val name = taskResourceLoader.getName(taskInfo)
+ val icon = taskResourceLoader.getHeaderIcon(taskInfo)
+ withContext(mainDispatcher) {
+ if (!isActive) return@withContext
+ handleMenuView.setAppName(name)
+ handleMenuView.setAppIcon(icon)
+ }
+ }
val x = handleMenuPosition.x.toInt()
val y = handleMenuPosition.y.toInt()
handleMenuViewContainer =
@@ -240,7 +263,19 @@ class HandleMenu(
val menuX: Int
val menuY: Int
val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
- updateGlobalMenuPosition(taskBounds, captionX, captionY)
+ globalMenuPosition.set(
+ calculateMenuPosition(
+ splitScreenController,
+ taskInfo,
+ marginStart = marginMenuStart,
+ marginMenuTop,
+ captionX,
+ captionY,
+ captionWidth,
+ menuWidth,
+ context.isRtl()
+ )
+ )
if (layoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the start of the header.
menuX = if (context.isRtl()) {
@@ -265,53 +300,6 @@ class HandleMenu(
handleMenuPosition.set(menuX.toFloat(), menuY.toFloat())
}
- private fun updateGlobalMenuPosition(taskBounds: Rect, captionX: Int, captionY: Int) {
- val nonFreeformX = captionX + (captionWidth / 2) - (menuWidth / 2)
- when {
- taskInfo.isFreeform -> {
- if (context.isRtl()) {
- globalMenuPosition.set(
- /* x= */ taskBounds.right - menuWidth - marginMenuStart,
- /* y= */ taskBounds.top + captionY + marginMenuTop
- )
- } else {
- globalMenuPosition.set(
- /* x= */ taskBounds.left + marginMenuStart,
- /* y= */ taskBounds.top + captionY + marginMenuTop
- )
- }
- }
- taskInfo.isFullscreen -> {
- globalMenuPosition.set(
- /* x = */ nonFreeformX,
- /* y = */ marginMenuTop + captionY
- )
- }
- taskInfo.isMultiWindow -> {
- val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
- val leftOrTopStageBounds = Rect()
- val rightOrBottomStageBounds = Rect()
- splitScreenController.getStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
- when (splitPosition) {
- SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
- globalMenuPosition.set(
- /* x = */ leftOrTopStageBounds.width() + nonFreeformX,
- /* y = */ captionY + marginMenuTop
- )
- }
- SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
- globalMenuPosition.set(
- /* x = */ nonFreeformX,
- /* y = */ captionY + marginMenuTop
- )
- }
- }
- }
- }
- }
-
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
@@ -374,8 +362,6 @@ class HandleMenu(
)
if (splitScreenController.getSplitPosition(taskInfo.taskId)
== SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- // TODO(b/343561161): This also needs to be calculated differently if
- // the task is in top/bottom split.
val leftStageBounds = Rect()
splitScreenController.getStageBounds(leftStageBounds, Rect())
inputRelativeToMenu.x += leftStageBounds.width().toFloat()
@@ -449,6 +435,7 @@ class HandleMenu(
resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
fun close() {
+ loadAppInfoJob?.cancel()
handleMenuView?.animateCloseMenu {
handleMenuViewContainer?.releaseView()
handleMenuViewContainer = null
@@ -476,8 +463,10 @@ class HandleMenu(
private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
R.id.collapse_menu_button)
- private val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
- private val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
+ @VisibleForTesting
+ val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+ @VisibleForTesting
+ val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
// Windowing Pill.
private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
@@ -546,14 +535,12 @@ class HandleMenu(
/** Binds the menu views to the new data. */
fun bind(
taskInfo: RunningTaskInfo,
- appIconBitmap: Bitmap?,
- appName: CharSequence?,
shouldShowMoreActionsPill: Boolean
) {
this.taskInfo = taskInfo
this.style = calculateMenuStyle(taskInfo)
- bindAppInfoPill(style, appIconBitmap, appName)
+ bindAppInfoPill(style)
if (shouldShowWindowingPill) {
bindWindowingPill(style)
}
@@ -564,6 +551,16 @@ class HandleMenu(
bindOpenInAppOrBrowserPill(style)
}
+ /** Sets the app's name. */
+ fun setAppName(name: CharSequence) {
+ appNameView.text = name
+ }
+
+ /** Sets the app's icon. */
+ fun setAppIcon(icon: Bitmap) {
+ appIconView.setImageBitmap(icon)
+ }
+
/** Animates the menu openInAppOrBrowserg. */
fun animateOpenMenu() {
if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
@@ -630,22 +627,14 @@ class HandleMenu(
)
}
- private fun bindAppInfoPill(
- style: MenuStyle,
- appIconBitmap: Bitmap?,
- appName: CharSequence?
- ) {
+ private fun bindAppInfoPill(style: MenuStyle) {
appInfoPill.background.setTint(style.backgroundColor)
collapseMenuButton.apply {
imageTintList = ColorStateList.valueOf(style.textColor)
this.taskInfo = this@HandleMenuView.taskInfo
}
- appIconView.setImageBitmap(appIconBitmap)
- appNameView.apply {
- text = appName
- setTextColor(style.textColor)
- }
+ appNameView.setTextColor(style.textColor)
}
private fun bindWindowingPill(style: MenuStyle) {
@@ -705,7 +694,7 @@ class HandleMenu(
setTextColor(style.textColor)
compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
}
-
+ openByDefaultBtn.isGone = isBrowserApp
openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor)
}
@@ -735,11 +724,12 @@ class HandleMenu(
/** A factory interface to create a [HandleMenu]. */
interface HandleMenuFactory {
fun create(
+ @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread bgScope: CoroutineScope,
parentDecor: DesktopModeWindowDecoration,
windowManagerWrapper: WindowManagerWrapper,
+ taskResourceLoader: WindowDecorTaskResourceLoader,
layoutResId: Int,
- appIconBitmap: Bitmap?,
- appName: CharSequence?,
splitScreenController: SplitScreenController,
shouldShowWindowingPill: Boolean,
shouldShowNewWindowButton: Boolean,
@@ -758,11 +748,12 @@ interface HandleMenuFactory {
/** A [HandleMenuFactory] implementation that creates a [HandleMenu]. */
object DefaultHandleMenuFactory : HandleMenuFactory {
override fun create(
+ @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread bgScope: CoroutineScope,
parentDecor: DesktopModeWindowDecoration,
windowManagerWrapper: WindowManagerWrapper,
+ taskResourceLoader: WindowDecorTaskResourceLoader,
layoutResId: Int,
- appIconBitmap: Bitmap?,
- appName: CharSequence?,
splitScreenController: SplitScreenController,
shouldShowWindowingPill: Boolean,
shouldShowNewWindowButton: Boolean,
@@ -777,11 +768,12 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
captionY: Int,
): HandleMenu {
return HandleMenu(
+ mainDispatcher,
+ bgScope,
parentDecor,
windowManagerWrapper,
+ taskResourceLoader,
layoutResId,
- appIconBitmap,
- appName,
splitScreenController,
shouldShowWindowingPill,
shouldShowNewWindowButton,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 376cd2a78baf..e23ebe6634ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -26,6 +26,7 @@ import android.graphics.drawable.RippleDrawable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewStub
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ProgressBar
@@ -46,13 +47,17 @@ class MaximizeButtonView(
private val hoverProgressAnimatorSet = AnimatorSet()
var hoverDisabled = false
- private val progressBar: ProgressBar
+ private lateinit var stubProgressBarContainer: ViewStub
private val maximizeWindow: ImageButton
+ private val progressBar: ProgressBar by lazy {
+ (stubProgressBarContainer.inflate() as FrameLayout)
+ .requireViewById(R.id.progress_bar)
+ }
init {
LayoutInflater.from(context).inflate(R.layout.maximize_menu_button, this, true)
- progressBar = requireViewById(R.id.progress_bar)
+ stubProgressBarContainer = requireViewById(R.id.stub_progress_bar_container)
maximizeWindow = requireViewById(R.id.maximize_window)
}
@@ -115,21 +120,34 @@ class MaximizeButtonView(
requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
maximizeWindow.imageTintList = iconForegroundColor
maximizeWindow.background = rippleDrawable
- progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
- .withAlpha(OPACITY_15)
- progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
+ stubProgressBarContainer.setOnInflateListener { _, inflated ->
+ val progressBar = (inflated as FrameLayout)
+ .requireViewById(R.id.progress_bar) as ProgressBar
+ progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
+ .withAlpha(OPACITY_15)
+ progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
+ }
} else {
- if (darkMode) {
- progressBar.progressTintList = ColorStateList.valueOf(
+ val progressTint = if (darkMode) {
+ ColorStateList.valueOf(
resources.getColor(R.color.desktop_mode_maximize_menu_progress_dark))
- maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
- R.color.desktop_mode_caption_button_color_selector_dark))
} else {
- progressBar.progressTintList = ColorStateList.valueOf(
+ ColorStateList.valueOf(
resources.getColor(R.color.desktop_mode_maximize_menu_progress_light))
- maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
- R.color.desktop_mode_caption_button_color_selector_light))
}
+ val backgroundTint = if (darkMode) {
+ ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_dark)
+ } else {
+ ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_light)
+ }
+ stubProgressBarContainer.setOnInflateListener { _, inflated ->
+ val progressBar = (inflated as FrameLayout)
+ .requireViewById(R.id.progress_bar) as ProgressBar
+ progressBar.progressTintList = progressTint
+ }
+ maximizeWindow.background?.setTintList(backgroundTint)
}
}
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 11a7cf8da8d8..cc54d25b3639 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
@@ -63,7 +63,7 @@ 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.calculateMaximizeBounds
+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
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
@@ -231,11 +231,7 @@ class MaximizeMenu(
}
private fun getSizeToggleDirection(): MaximizeMenuView.SizeToggleDirection {
- val maximizeBounds = calculateMaximizeBounds(
- displayController.getDisplayLayout(taskInfo.displayId)!!,
- taskInfo
- )
- val maximized = taskInfo.configuration.windowConfiguration.bounds.equals(maximizeBounds)
+ val maximized = isTaskMaximized(taskInfo, displayController)
return if (maximized)
MaximizeMenuView.SizeToggleDirection.RESTORE
else
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
index 8770d35cb85c..96839ce47725 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
-import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.PointF
@@ -38,13 +37,23 @@ import android.window.TaskConstants
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.ui.graphics.toArgb
+import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.Theme
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
/**
* Creates and updates a veil that covers task contents on resize.
@@ -52,7 +61,9 @@ import java.util.function.Supplier
public class ResizeVeil @JvmOverloads constructor(
private val context: Context,
private val displayController: DisplayController,
- private val appIcon: Bitmap,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
+ @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
private var parentSurface: SurfaceControl,
private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
@@ -65,7 +76,8 @@ public class ResizeVeil @JvmOverloads constructor(
private val lightColors = dynamicLightColorScheme(context)
private val darkColors = dynamicDarkColorScheme(context)
- private lateinit var iconView: ImageView
+ @VisibleForTesting
+ lateinit var iconView: ImageView
private var iconSize = 0
/** A container surface to host the veil background and icon child surfaces. */
@@ -77,6 +89,7 @@ public class ResizeVeil @JvmOverloads constructor(
private var viewHost: SurfaceControlViewHost? = null
private var display: Display? = null
private var veilAnimator: ValueAnimator? = null
+ private var loadAppInfoJob: Job? = null
/**
* Whether the resize veil is currently visible.
@@ -142,7 +155,6 @@ public class ResizeVeil @JvmOverloads constructor(
val root = LayoutInflater.from(context)
.inflate(R.layout.desktop_mode_resize_veil, null /* root */)
iconView = root.requireViewById(R.id.veil_application_icon)
- iconView.setImageBitmap(appIcon)
val lp = WindowManager.LayoutParams(
iconSize,
iconSize,
@@ -156,6 +168,14 @@ public class ResizeVeil @JvmOverloads constructor(
iconSurface, null /* hostInputToken */)
viewHost = surfaceControlViewHostFactory.create(context, display, wwm, "ResizeVeil")
viewHost?.setView(root, lp)
+ loadAppInfoJob = bgScope.launch {
+ if (!isActive) return@launch
+ val icon = taskResourceLoader.getVeilIcon(taskInfo)
+ withContext(mainDispatcher) {
+ if (!isActive) return@withContext
+ iconView.setImageBitmap(icon)
+ }
+ }
Trace.endSection()
}
@@ -401,6 +421,7 @@ public class ResizeVeil @JvmOverloads constructor(
cancelAnimation()
veilAnimator = null
isVisible = false
+ loadAppInfoJob?.cancel()
viewHost?.release()
viewHost = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 1f03d7568130..e011cc08903b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -104,7 +104,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
}
@Override
- public Rect onDragPositioningStart(int ctrlType, float x, float y) {
+ public Rect onDragPositioningStart(int ctrlType, int displayId, float x, float y) {
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -136,7 +136,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
}
@Override
- public Rect onDragPositioningMove(float x, float y) {
+ public Rect onDragPositioningMove(int displayId, float x, float y) {
if (Looper.myLooper() != mHandler.getLooper()) {
// This method must run on the shell main thread to use the correct Choreographer
// instance below.
@@ -170,7 +170,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
}
@Override
- public Rect onDragPositioningEnd(float x, float y) {
+ public Rect onDragPositioningEnd(int displayId, float x, float y) {
PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
mRepositionStartPoint);
if (isResizing()) {
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 584ee39ab317..5d1bedb85b5e 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
@@ -61,6 +61,8 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import java.util.ArrayList;
@@ -88,10 +90,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
implements AutoCloseable {
/**
- * The Z-order of {@link #mCaptionContainerSurface}.
+ * The Z-order of the caption surface.
* <p>
* We use {@link #mDecorationContainerSurface} to define input window for task resizing; by
- * layering it in front of {@link #mCaptionContainerSurface}, we can allow it to handle input
+ * layering it in front of the caption surface, we can allow it to handle input
* prior to caption view itself, treating corner inputs as resize events rather than
* repositioning.
*/
@@ -100,7 +102,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
* The Z-order of the task input sink in {@link DragPositioningCallback}.
* <p>
* This task input sink is used to prevent undesired dispatching of motion events out of task
- * bounds; by layering it behind {@link #mCaptionContainerSurface}, we allow captions to handle
+ * bounds; by layering it behind the caption surface, we allow captions to handle
* input events first.
*/
static final int INPUT_SINK_Z_ORDER = -2;
@@ -126,6 +128,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
+ @NonNull private final WindowDecorViewHostSupplier<WindowDecorViewHost>
+ mWindowDecorViewHostSupplier;
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@Override
@@ -150,9 +154,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
Display mDisplay;
SurfaceControl mDecorationContainerSurface;
- SurfaceControl mCaptionContainerSurface;
- private CaptionWindowlessWindowManager mCaptionWindowManager;
- private SurfaceControlViewHost mViewHost;
+ private WindowDecorViewHost mViewHost;
private Configuration mWindowDecorConfig;
TaskDragResizer mTaskDragResizer;
boolean mIsCaptionVisible;
@@ -173,11 +175,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
- SurfaceControl taskSurface) {
- this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ SurfaceControl taskSurface,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
+ this(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, SurfaceControl::new,
- new SurfaceControlViewHostFactory() {}, new DesktopModeEventLogger());
+ new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier,
+ new DesktopModeEventLogger());
}
WindowDecoration(
@@ -192,6 +196,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
@NonNull DesktopModeEventLogger desktopModeEventLogger
) {
mContext = context;
@@ -205,6 +210,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId);
mIsStatusBarVisible = insetsState != null
@@ -240,15 +246,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
RelayoutResult<T> outResult) {
- updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult);
- if (outResult.mRootView != null) {
- updateViewHost(params, startT, outResult);
- }
- }
-
- protected void updateViewsAndSurfaces(RelayoutParams params,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) {
+ Trace.beginSection("WindowDecoration#relayout");
outResult.reset();
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
@@ -263,17 +261,21 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (params.mSetTaskVisibilityPositionAndCrop) {
finishT.hide(mTaskSurface);
}
+ Trace.endSection(); // WindowDecoration#relayout
return;
}
-
+ Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded");
inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
- if (outResult.mRootView == null) {
- // Didn't manage to create a root view, early out.
+ Trace.endSection();
+ final boolean hasCaptionView = outResult.mRootView != null;
+ if (!hasCaptionView) {
+ Trace.endSection(); // WindowDecoration#relayout
return;
}
- rootView = null; // Clear it just in case we use it accidentally
+ Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
updateCaptionVisibility(outResult.mRootView, params);
+ Trace.endSection();
final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
outResult.mWidth = taskBounds.width();
@@ -288,10 +290,65 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
outResult.mCaptionY = 0;
outResult.mCaptionTopPadding = params.mCaptionTopPadding;
+ Trace.beginSection("relayout-createViewHostIfNeeded");
+ createViewHostIfNeeded(mDecorWindowContext, mDisplay);
+ Trace.endSection();
+
+ Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets");
+ final SurfaceControl captionSurface = mViewHost.getSurfaceControl();
updateDecorationContainerSurface(startT, outResult);
- updateCaptionContainerSurface(startT, outResult);
+ updateCaptionContainerSurface(captionSurface, startT, outResult);
updateCaptionInsets(params, wct, outResult, taskBounds);
updateTaskSurface(params, startT, finishT, outResult);
+ Trace.endSection();
+
+ Trace.beginSection("WindowDecoration#relayout-updateViewHost");
+ outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+ final Rect localCaptionBounds = new Rect(
+ outResult.mCaptionX,
+ outResult.mCaptionY,
+ outResult.mCaptionX + outResult.mCaptionWidth,
+ outResult.mCaptionY + outResult.mCaptionHeight);
+ final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
+ ? calculateLimitedTouchableRegion(params, localCaptionBounds)
+ : null;
+ updateViewHierarchy(params, outResult, startT, touchableRegion);
+ Trace.endSection();
+
+ Trace.endSection(); // WindowDecoration#relayout
+ }
+
+ private void createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) {
+ if (mViewHost == null) {
+ mViewHost = mWindowDecorViewHostSupplier.acquire(context, display);
+ }
+ }
+
+ private void updateViewHierarchy(@NonNull RelayoutParams params,
+ @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT,
+ @Nullable Region touchableRegion) {
+ Trace.beginSection("WindowDecoration#updateViewHierarchy");
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(
+ outResult.mCaptionWidth,
+ outResult.mCaptionHeight,
+ TYPE_APPLICATION,
+ FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSPARENT);
+ lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+ lp.setTrustedOverlay();
+ lp.inputFeatures = params.mInputFeatures;
+ if (params.mAsyncViewHost) {
+ if (params.mApplyStartTransactionOnDraw) {
+ throw new IllegalArgumentException("Cannot use sync draw tx with async relayout");
+ }
+ mViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.configuration,
+ touchableRegion);
+ } else {
+ mViewHost.updateView(outResult.mRootView, lp, mTaskInfo.configuration,
+ touchableRegion, params.mApplyStartTransactionOnDraw ? startT : null);
+ }
+ Trace.endSection();
}
private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
@@ -359,23 +416,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.show(mDecorationContainerSurface);
}
- private void updateCaptionContainerSurface(
+ private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface,
SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
- if (mCaptionContainerSurface == null) {
- final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
- mCaptionContainerSurface = builder
- .setName("Caption container of Task=" + mTaskInfo.taskId)
- .setContainerLayer()
- .setParent(mDecorationContainerSurface)
- .setCallsite("WindowDecoration.updateCaptionContainerSurface")
- .build();
- }
-
- startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
- outResult.mCaptionHeight)
- .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
- .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
- .show(mCaptionContainerSurface);
+ startT.reparent(captionSurface, mDecorationContainerSurface)
+ .setWindowCrop(captionSurface, outResult.mCaptionWidth, outResult.mCaptionHeight)
+ .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */)
+ .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER)
+ .show(captionSurface);
}
private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
@@ -469,80 +516,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
}
- /**
- * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our
- * View hierarchy.
- *
- * @param params parameters to use from the last relayout
- * @param onDrawTransaction a transaction to apply in sync with #onDraw
- * @param outResult results to use from the last relayout
- *
- */
- protected void updateViewHost(RelayoutParams params,
- SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) {
- Trace.beginSection("CaptionViewHostLayout");
- if (mCaptionWindowManager == null) {
- // Put caption under a container surface because ViewRootImpl sets the destination frame
- // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
- mCaptionWindowManager = new CaptionWindowlessWindowManager(
- mTaskInfo.getConfiguration(), mCaptionContainerSurface);
- }
- mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
- final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(
- outResult.mCaptionWidth,
- outResult.mCaptionHeight,
- TYPE_APPLICATION,
- FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSPARENT);
- lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
- lp.setTrustedOverlay();
- lp.inputFeatures = params.mInputFeatures;
- final Rect localCaptionBounds = new Rect(
- outResult.mCaptionX,
- outResult.mCaptionY,
- outResult.mCaptionX + outResult.mCaptionWidth,
- outResult.mCaptionY + outResult.mCaptionHeight);
- final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
- ? calculateLimitedTouchableRegion(params, localCaptionBounds)
- : null;
- if (mViewHost == null) {
- Trace.beginSection("CaptionViewHostLayout-new");
- mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
- mCaptionWindowManager);
- if (params.mApplyStartTransactionOnDraw) {
- if (onDrawTransaction == null) {
- throw new IllegalArgumentException("Trying to sync a null Transaction");
- }
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
- }
- outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
- if (params.mLimitTouchRegionToSystemAreas) {
- mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
- }
- mViewHost.setView(outResult.mRootView, lp);
- Trace.endSection();
- } else {
- Trace.beginSection("CaptionViewHostLayout-relayout");
- if (params.mApplyStartTransactionOnDraw) {
- if (onDrawTransaction == null) {
- throw new IllegalArgumentException("Trying to sync a null Transaction");
- }
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
- }
- outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
- if (params.mLimitTouchRegionToSystemAreas) {
- mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
- }
- mViewHost.relayout(lp);
- Trace.endSection();
- }
- if (touchableRegion != null) {
- touchableRegion.recycle();
- }
- Trace.endSection(); // CaptionViewHostLayout
- }
-
@NonNull
private Region calculateLimitedTouchableRegion(
RelayoutParams params,
@@ -691,18 +664,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
void releaseViews(WindowContainerTransaction wct) {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- mCaptionWindowManager = null;
-
final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
boolean released = false;
- if (mCaptionContainerSurface != null) {
- t.remove(mCaptionContainerSurface);
- mCaptionContainerSurface = null;
+ if (mViewHost != null) {
+ mWindowDecorViewHostSupplier.release(mViewHost, t);
+ mViewHost = null;
released = true;
}
@@ -855,6 +821,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
boolean mIsCaptionVisible;
Configuration mWindowDecorConfig;
+ boolean mAsyncViewHost;
boolean mApplyStartTransactionOnDraw;
boolean mSetTaskVisibilityPositionAndCrop;
@@ -880,6 +847,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mApplyStartTransactionOnDraw = false;
mSetTaskVisibilityPositionAndCrop = false;
mWindowDecorConfig = null;
+ mAsyncViewHost = false;
mHasGlobalFocus = false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt
new file mode 100644
index 000000000000..6def3daf8c8d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.windowdecor.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Point
+import android.graphics.Rect
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.windowdecor.extension.isFullscreen
+
+/** Utility function used for calculating position of desktop mode menus. */
+fun calculateMenuPosition(
+ splitScreenController: SplitScreenController,
+ taskInfo: RunningTaskInfo,
+ marginStart: Int,
+ marginTop: Int,
+ captionX: Int,
+ captionY: Int,
+ captionWidth: Int,
+ menuWidth: Int,
+ isRtl: Boolean,
+): Point {
+ if (taskInfo.isFreeform) {
+ val taskBounds = taskInfo.configuration.windowConfiguration.bounds
+ return if (isRtl) {
+ Point(
+ /* x= */ taskBounds.right - menuWidth - marginStart,
+ /* y= */ taskBounds.top + marginTop,
+ )
+ } else {
+ Point(/* x= */ taskBounds.left + marginStart, /* y= */ taskBounds.top + marginTop)
+ }
+ }
+ val nonFreeformPosition = Point(captionX + (captionWidth / 2) - (menuWidth / 2), captionY)
+ if (taskInfo.isFullscreen) {
+ return Point(nonFreeformPosition.x, nonFreeformPosition.y + marginTop)
+ }
+ // Only the splitscreen case left.
+ val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
+ val leftOrTopStageBounds = Rect()
+ val rightOrBottomStageBounds = Rect()
+ splitScreenController.getRefStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
+ if (splitScreenController.isLeftRightSplit) {
+ val rightStageModifier =
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ rightOrBottomStageBounds.left
+ } else {
+ 0
+ }
+ return Point(
+ /* x = */ rightStageModifier + nonFreeformPosition.x,
+ /* y = */ nonFreeformPosition.y + marginTop,
+ )
+ } else {
+ val bottomSplitModifier =
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ rightOrBottomStageBounds.top
+ } else {
+ 0
+ }
+ return Point(
+ /* x = */ nonFreeformPosition.x,
+ /* y = */ nonFreeformPosition.y + bottomSplitModifier + marginTop,
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
new file mode 100644
index 000000000000..d87da092cccf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.windowdecor.common
+
+import android.annotation.DimenRes
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.os.UserHandle
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.R
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+
+/**
+ * A utility and cache for window decoration UI resources.
+ */
+class WindowDecorTaskResourceLoader(
+ private val context: Context,
+ shellInit: ShellInit,
+ private val shellController: ShellController,
+ private val shellCommandHandler: ShellCommandHandler,
+ private val iconProvider: IconProvider,
+ private val headerIconFactory: BaseIconFactory,
+ private val veilIconFactory: BaseIconFactory,
+) {
+ constructor(
+ context: Context,
+ shellInit: ShellInit,
+ shellController: ShellController,
+ shellCommandHandler: ShellCommandHandler,
+ ) : this(
+ context,
+ shellInit,
+ shellController,
+ shellCommandHandler,
+ IconProvider(context),
+ headerIconFactory = context.createIconFactory(R.dimen.desktop_mode_caption_icon_radius),
+ veilIconFactory = context.createIconFactory(R.dimen.desktop_mode_resize_veil_icon_size),
+ )
+
+ /**
+ * A map of task -> resources to prevent unnecessary binder calls and resource loading
+ * when multiple window decorations need the same resources, for example, the app name or icon
+ * used in the header and menu.
+ */
+ @VisibleForTesting
+ val taskToResourceCache = ConcurrentHashMap<Int, AppResources>()
+ /**
+ * Keeps track of existing tasks with a window decoration. Useful to verify that requests to
+ * get resources occur within the lifecycle of a window decoration, otherwise it'd be possible
+ * to load a tasks resources into memory without a future signal to clean up the resource.
+ * See [onWindowDecorClosed].
+ */
+ private val existingTasks = mutableSetOf<Int>()
+
+ @VisibleForTesting
+ lateinit var currentUserContext: Context
+
+ init {
+ shellInit.addInitCallback(this::onInit, this)
+ }
+
+ private fun onInit() {
+ shellCommandHandler.addDumpCallback(this::dump, this)
+ shellController.addUserChangeListener(object : UserChangeListener {
+ override fun onUserChanged(newUserId: Int, userContext: Context) {
+ currentUserContext = userContext
+ // No need to hold on to resources for tasks of another profile.
+ taskToResourceCache.clear()
+ }
+ })
+ currentUserContext = context.createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0
+ )
+ }
+
+ /** Returns the user readable name for this task. */
+ @ShellBackgroundThread
+ fun getName(taskInfo: RunningTaskInfo): CharSequence {
+ checkWindowDecorExists(taskInfo)
+ val cachedResources = taskToResourceCache[taskInfo.taskId]
+ if (cachedResources != null) {
+ return cachedResources.appName
+ }
+ val resources = loadAppResources(taskInfo)
+ taskToResourceCache[taskInfo.taskId] = resources
+ return resources.appName
+ }
+
+ /** Returns the icon for use by the app header and menus for this task. */
+ @ShellBackgroundThread
+ fun getHeaderIcon(taskInfo: RunningTaskInfo): Bitmap {
+ checkWindowDecorExists(taskInfo)
+ val cachedResources = taskToResourceCache[taskInfo.taskId]
+ if (cachedResources != null) {
+ return cachedResources.appIcon
+ }
+ val resources = loadAppResources(taskInfo)
+ taskToResourceCache[taskInfo.taskId] = resources
+ return resources.appIcon
+ }
+
+ /** Returns the icon for use by the resize veil for this task. */
+ @ShellBackgroundThread
+ fun getVeilIcon(taskInfo: RunningTaskInfo): Bitmap {
+ checkWindowDecorExists(taskInfo)
+ val cachedResources = taskToResourceCache[taskInfo.taskId]
+ if (cachedResources != null) {
+ return cachedResources.veilIcon
+ }
+ val resources = loadAppResources(taskInfo)
+ taskToResourceCache[taskInfo.taskId] = resources
+ return resources.veilIcon
+ }
+
+ /** Called when a window decoration for this task is created. */
+ fun onWindowDecorCreated(taskInfo: RunningTaskInfo) {
+ existingTasks.add(taskInfo.taskId)
+ }
+
+ /** Called when a window decoration for this task is closed. */
+ fun onWindowDecorClosed(taskInfo: RunningTaskInfo) {
+ existingTasks.remove(taskInfo.taskId)
+ taskToResourceCache.remove(taskInfo.taskId)
+ }
+
+ private fun checkWindowDecorExists(taskInfo: RunningTaskInfo) {
+ check(existingTasks.contains(taskInfo.taskId)) {
+ "Attempt to obtain resource for non-existent decoration"
+ }
+ }
+
+ private fun loadAppResources(taskInfo: RunningTaskInfo): AppResources {
+ Trace.beginSection("$TAG#loadAppResources")
+ val pm = currentUserContext.packageManager
+ val activityInfo = getActivityInfo(taskInfo, pm)
+ val appName = pm.getApplicationLabel(activityInfo.applicationInfo)
+ val appIconDrawable = iconProvider.getIcon(activityInfo)
+ val badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable, taskInfo.userHandle())
+ val appIcon = headerIconFactory.createIconBitmap(badgedAppIconDrawable, /* scale= */ 1f)
+ val veilIcon = veilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+ Trace.endSection()
+ return AppResources(appName = appName, appIcon = appIcon, veilIcon = veilIcon)
+ }
+
+ private fun getActivityInfo(taskInfo: RunningTaskInfo, pm: PackageManager): ActivityInfo {
+ return pm.getActivityInfo(taskInfo.component(), /* flags= */ 0)
+ }
+
+ private fun RunningTaskInfo.component() = baseIntent.component!!
+
+ private fun RunningTaskInfo.userHandle() = UserHandle.of(userId)
+
+ data class AppResources(val appName: CharSequence, val appIcon: Bitmap, val veilIcon: Bitmap)
+
+ private fun dump(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}$TAG")
+ pw.println(innerPrefix + "appResourceCache=$taskToResourceCache")
+ pw.println(innerPrefix + "existingTasks=$existingTasks")
+ }
+
+ companion object {
+ private const val TAG = "AppResourceProvider"
+ }
+}
+
+/** Creates an icon factory with the provided [dimensions]. */
+fun Context.createIconFactory(@DimenRes dimensions: Int): BaseIconFactory {
+ val densityDpi = resources.displayMetrics.densityDpi
+ val iconSize = resources.getDimensionPixelSize(dimensions)
+ return BaseIconFactory(this, densityDpi, iconSize)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
index c470eef2578c..a205ac662ab1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
@@ -17,12 +17,11 @@ package com.android.wm.shell.windowdecor.common.viewhost
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Region
import android.view.Display
import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
-import android.view.WindowlessWindowManager
import androidx.tracing.Trace
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -30,51 +29,34 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-typealias SurfaceControlViewHostFactory =
- (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
-
/**
- * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
+ * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHostAdapter].
*
- * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
- * any attempts to do will throw, which means that once a [View] is added using [updateView] or
- * [updateViewAsync], only its properties and binding may be changed, its children views may be
- * added, removed or changed and its [WindowManager.LayoutParams] may be changed. It also supports
- * asynchronously updating the view hierarchy using [updateViewAsync], in which case the update work
- * will be posted on the [ShellMainThread] with no delay.
+ * It supports asynchronously updating the view hierarchy using [updateViewAsync], in which
+ * case the update work will be posted on the [ShellMainThread] with no delay.
*/
class DefaultWindowDecorViewHost(
- private val context: Context,
+ context: Context,
@ShellMainThread private val mainScope: CoroutineScope,
- private val display: Display,
- private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
- SurfaceControlViewHost(c, d, wwm, s)
- },
+ display: Display,
+ @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter =
+ SurfaceControlViewHostAdapter(context, display),
) : WindowDecorViewHost {
-
- private val rootSurface: SurfaceControl =
- SurfaceControl.Builder()
- .setName("DefaultWindowDecorViewHost surface")
- .setContainerLayer()
- .setCallsite("DefaultWindowDecorViewHost#init")
- .build()
-
- private var wwm: WindowlessWindowManager? = null
- @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
private var currentUpdateJob: Job? = null
override val surfaceControl: SurfaceControl
- get() = rootSurface
+ get() = viewHostAdapter.rootSurface
override fun updateView(
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
onDrawTransaction: SurfaceControl.Transaction?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateView")
clearCurrentUpdateJob()
- updateViewHost(view, attrs, configuration, onDrawTransaction)
+ updateViewHost(view, attrs, configuration, touchableRegion, onDrawTransaction)
Trace.endSection()
}
@@ -82,53 +64,41 @@ class DefaultWindowDecorViewHost(
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
clearCurrentUpdateJob()
currentUpdateJob =
mainScope.launch {
- updateViewHost(view, attrs, configuration, onDrawTransaction = null)
+ updateViewHost(
+ view,
+ attrs,
+ configuration,
+ touchableRegion,
+ onDrawTransaction = null
+ )
}
Trace.endSection()
}
override fun release(t: SurfaceControl.Transaction) {
clearCurrentUpdateJob()
- viewHost?.release()
- t.remove(rootSurface)
+ viewHostAdapter.release(t)
}
private fun updateViewHost(
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
onDrawTransaction: SurfaceControl.Transaction?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
- if (wwm == null) {
- wwm = WindowlessWindowManager(configuration, rootSurface, null)
- }
- requireWindowlessWindowManager().setConfiguration(configuration)
- if (viewHost == null) {
- viewHost =
- surfaceControlViewHostFactory.invoke(
- context,
- display,
- requireWindowlessWindowManager(),
- "DefaultWindowDecorViewHost#updateViewHost",
- )
- }
- onDrawTransaction?.let { requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) }
- if (requireViewHost().view == null) {
- Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
- requireViewHost().setView(view, attrs)
- Trace.endSection()
- } else {
- check(requireViewHost().view == view) { "Changing view is not allowed" }
- Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
- requireViewHost().relayout(attrs)
- Trace.endSection()
+ viewHostAdapter.prepareViewHost(configuration, touchableRegion)
+ onDrawTransaction?.let {
+ viewHostAdapter.applyTransactionOnDraw(it)
}
+ viewHostAdapter.updateView(view, attrs)
Trace.endSection()
}
@@ -136,12 +106,4 @@ class DefaultWindowDecorViewHost(
currentUpdateJob?.cancel()
currentUpdateJob = null
}
-
- private fun requireWindowlessWindowManager(): WindowlessWindowManager {
- return wwm ?: error("Expected non-null windowless window manager")
- }
-
- private fun requireViewHost(): SurfaceControlViewHost {
- return viewHost ?: error("Expected non-null view host")
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
index 27ffd6cd8076..7821619e61d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
@@ -24,14 +24,15 @@ import kotlinx.coroutines.CoroutineScope
/**
* A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
*/
-class DefaultWindowDecorViewHostSupplier(@ShellMainThread private val mainScope: CoroutineScope) :
- WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {
+class DefaultWindowDecorViewHostSupplier(
+ @ShellMainThread private val mainScope: CoroutineScope
+) : WindowDecorViewHostSupplier<WindowDecorViewHost> {
- override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
+ override fun acquire(context: Context, display: Display): WindowDecorViewHost {
return DefaultWindowDecorViewHost(context, mainScope, display)
}
- override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
+ override fun release(viewHost: WindowDecorViewHost, t: SurfaceControl.Transaction) {
viewHost.release(t)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt
new file mode 100644
index 000000000000..47cfaeed6157
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.content.Context
+import android.os.Trace
+import android.util.Pools
+import android.view.Display
+import android.view.SurfaceControl
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A [WindowDecorViewHostSupplier] backed by a pool to allow recycling view hosts which may be
+ * expensive to recreate for each new or updated window decoration.
+ *
+ * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled object if
+ * available, or create a new instance and return it if needed. When finished using a
+ * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back into the
+ * pool and reused later on.
+ *
+ * This class also supports pre-warming [ReusableWindowDecorViewHost] instances, which will be put
+ * into the pool immediately after creation.
+ */
+class PooledWindowDecorViewHostSupplier(
+ private val context: Context,
+ @ShellMainThread private val mainScope: CoroutineScope,
+ shellInit: ShellInit,
+ maxPoolSize: Int,
+ private val preWarmSize: Int,
+) : WindowDecorViewHostSupplier<WindowDecorViewHost> {
+
+ private val pool: Pools.Pool<WindowDecorViewHost> = Pools.SynchronizedPool(maxPoolSize)
+ private var nextDecorViewHostId = 0
+
+ init {
+ require(preWarmSize <= maxPoolSize) { "Pre-warm size should not exceed pool size" }
+ shellInit.addInitCallback(this::onShellInit, this)
+ }
+
+ private fun onShellInit() {
+ if (preWarmSize <= 0) {
+ return
+ }
+ preWarmViewHosts(preWarmSize)
+ }
+
+ private fun preWarmViewHosts(preWarmSize: Int) {
+ mainScope.launch {
+ // Applying isn't needed, as the surface was never actually shown.
+ val t = SurfaceControl.Transaction()
+ repeat(preWarmSize) {
+ val warmedViewHost = newInstance(context, context.display).apply { warmUp() }
+ // Put the warmed view host in the pool by releasing it.
+ release(warmedViewHost, t)
+ }
+ }
+ }
+
+ override fun acquire(context: Context, display: Display): WindowDecorViewHost {
+ val pooledViewHost = pool.acquire()
+ if (pooledViewHost != null) {
+ return pooledViewHost
+ }
+ Trace.beginSection("PooledWindowDecorViewHostSupplier#acquire-newInstance")
+ val newDecorViewHost = newInstance(context, display)
+ Trace.endSection()
+ return newDecorViewHost
+ }
+
+ override fun release(viewHost: WindowDecorViewHost, t: SurfaceControl.Transaction) {
+ val pooled = pool.release(viewHost)
+ if (!pooled) {
+ viewHost.release(t)
+ }
+ }
+
+ private fun newInstance(context: Context, display: Display): ReusableWindowDecorViewHost {
+ // Use a reusable window decor view host, as it allows swapping the entire view hierarchy.
+ return ReusableWindowDecorViewHost(
+ context = context,
+ mainScope = mainScope,
+ display = display,
+ id = nextDecorViewHostId++,
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt
new file mode 100644
index 000000000000..da41e1b1d8d8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PixelFormat
+import android.graphics.Region
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION
+import android.widget.FrameLayout
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * An implementation of [WindowDecorViewHost] that supports:
+ * 1) Replacing the root [View], meaning [WindowDecorViewHost.updateView] maybe be called with
+ * different [View] instances. This is useful when reusing [WindowDecorViewHost]s instances for
+ * vastly different view hierarchies, such as Desktop Windowing's App Handles and App Headers.
+ * 2) Pre-warming of the underlying [SurfaceControlViewHostAdapter]s. Useful because their creation
+ * and first root view assignment are expensive, which is undesirable in latency-sensitive code
+ * paths like during a shell transition.
+ */
+class ReusableWindowDecorViewHost(
+ private val context: Context,
+ @ShellMainThread private val mainScope: CoroutineScope,
+ display: Display,
+ val id: Int,
+ @VisibleForTesting
+ val viewHostAdapter: SurfaceControlViewHostAdapter =
+ SurfaceControlViewHostAdapter(context, display),
+) : WindowDecorViewHost, Warmable {
+ @VisibleForTesting val rootView = FrameLayout(context)
+
+ private var currentUpdateJob: Job? = null
+
+ override val surfaceControl: SurfaceControl
+ get() = viewHostAdapter.rootSurface
+
+ override fun warmUp() {
+ if (viewHostAdapter.isInitialized()) {
+ // Already warmed up.
+ return
+ }
+ Trace.beginSection("$TAG#warmUp")
+ viewHostAdapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ viewHostAdapter.updateView(
+ rootView,
+ WindowManager.LayoutParams(
+ 0 /* width*/,
+ 0 /* height */,
+ TYPE_APPLICATION,
+ FLAG_NOT_FOCUSABLE or FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSPARENT,
+ )
+ .apply {
+ setTitle("View root of $TAG#$id")
+ setTrustedOverlay()
+ },
+ )
+ Trace.endSection()
+ }
+
+ override fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateView")
+ clearCurrentUpdateJob()
+ updateViewHost(view, attrs, configuration, touchableRegion, onDrawTransaction)
+ Trace.endSection()
+ }
+
+ override fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateViewAsync")
+ clearCurrentUpdateJob()
+ currentUpdateJob =
+ mainScope.launch {
+ updateViewHost(
+ view,
+ attrs,
+ configuration,
+ touchableRegion,
+ onDrawTransaction = null,
+ )
+ }
+ Trace.endSection()
+ }
+
+ override fun release(t: SurfaceControl.Transaction) {
+ clearCurrentUpdateJob()
+ viewHostAdapter.release(t)
+ }
+
+ private fun updateViewHost(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateViewHost")
+ viewHostAdapter.prepareViewHost(configuration, touchableRegion)
+ onDrawTransaction?.let { viewHostAdapter.applyTransactionOnDraw(it) }
+ rootView.removeAllViews()
+ rootView.addView(view)
+ viewHostAdapter.updateView(rootView, attrs)
+ Trace.endSection()
+ }
+
+ private fun clearCurrentUpdateJob() {
+ currentUpdateJob?.cancel()
+ currentUpdateJob = null
+ }
+
+ companion object {
+ private const val TAG = "ReusableWindowDecorViewHost"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt
new file mode 100644
index 000000000000..26a43f4d5291
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Region
+import android.view.AttachedSurfaceControl
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+
+typealias SurfaceControlViewHostFactory =
+ (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
+
+/**
+ * Adapter for a [SurfaceControlViewHost] and its backing [SurfaceControl].
+ *
+ * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
+ * any attempts to do will throw, which means that once a [View] is added using [updateView], only
+ * its properties and binding may be changed, children views may be added, removed or changed
+ * and its [WindowManager.LayoutParams] may be changed.
+ */
+class SurfaceControlViewHostAdapter(
+ private val context: Context,
+ private val display: Display,
+ private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
+ SurfaceControlViewHost(c, d, wwm, s)
+ },
+) {
+ val rootSurface: SurfaceControl =
+ SurfaceControl.Builder()
+ .setName("SurfaceControlViewHostAdapter surface")
+ .setContainerLayer()
+ .setCallsite("SurfaceControlViewHostAdapter#init")
+ .build()
+
+ private var wwm: WindowDecorWindowlessWindowManager? = null
+ @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
+
+ /**
+ * Initialize or updates the [SurfaceControlViewHost].
+ */
+ fun prepareViewHost(
+ configuration: Configuration,
+ touchableRegion: Region?
+ ) {
+ if (wwm == null) {
+ wwm = WindowDecorWindowlessWindowManager(configuration, rootSurface)
+ }
+ if (viewHost == null) {
+ viewHost =
+ surfaceControlViewHostFactory.invoke(
+ context,
+ display,
+ requireWindowlessWindowManager(),
+ "SurfaceControlViewHostAdapter#prepareViewHost",
+ )
+ }
+ requireWindowlessWindowManager().setConfiguration(configuration)
+ requireWindowlessWindowManager().setTouchRegion(requireViewHost(), touchableRegion)
+ }
+
+ /**
+ * Request to apply the transaction atomically with the next draw of the view hierarchy. See
+ * [AttachedSurfaceControl.applyTransactionOnDraw].
+ */
+ fun applyTransactionOnDraw(t: SurfaceControl.Transaction) {
+ requireViewHost().rootSurfaceControl.applyTransactionOnDraw(t)
+ }
+
+ /** Update the view hierarchy of the view host. */
+ fun updateView(view: View, attrs: WindowManager.LayoutParams) {
+ if (requireViewHost().view == null) {
+ Trace.beginSection("SurfaceControlViewHostAdapter#updateView-setView")
+ requireViewHost().setView(view, attrs)
+ Trace.endSection()
+ } else {
+ check(requireViewHost().view == view) { "Changing view is not allowed" }
+ Trace.beginSection("SurfaceControlViewHostAdapter#updateView-relayout")
+ requireViewHost().relayout(attrs)
+ Trace.endSection()
+ }
+ }
+
+ /** Release the view host and remove the backing surface. */
+ fun release(t: SurfaceControl.Transaction) {
+ viewHost?.release()
+ t.remove(rootSurface)
+ }
+
+ /** Whether the view host has had a view hierarchy set. */
+ fun isInitialized(): Boolean = viewHost?.view != null
+
+ private fun requireWindowlessWindowManager(): WindowDecorWindowlessWindowManager {
+ return wwm ?: error("Expected non-null windowless window manager")
+ }
+
+ private fun requireViewHost(): SurfaceControlViewHost {
+ return viewHost ?: error("Expected non-null view host")
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt
new file mode 100644
index 000000000000..2cb0f891436b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+/**
+ * An interface for an object that can be warmed up before it's needed.
+ */
+interface Warmable {
+ fun warmUp()
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
index 7c1479e9f9bd..2dcbbac4646f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor.common.viewhost
import android.content.res.Configuration
+import android.graphics.Region
import android.view.SurfaceControl
import android.view.View
import android.view.WindowManager
@@ -34,11 +35,17 @@ interface WindowDecorViewHost {
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
- onDrawTransaction: SurfaceControl.Transaction?,
+ touchableRegion: Region? = null,
+ onDrawTransaction: SurfaceControl.Transaction? = null,
)
/** Asynchronously update the view hierarchy of this view host. */
- fun updateViewAsync(view: View, attrs: WindowManager.LayoutParams, configuration: Configuration)
+ fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region? = null,
+ )
/** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
fun release(t: SurfaceControl.Transaction)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt
new file mode 100644
index 000000000000..fbe8c6c83b5c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.res.Configuration
+import android.graphics.Region
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.WindowlessWindowManager
+
+/**
+ * A [WindowlessWindowManager] for the window decor caption that allows customizing the touchable
+ * region.
+ */
+class WindowDecorWindowlessWindowManager(
+ configuration: Configuration,
+ rootSurface: SurfaceControl,
+) : WindowlessWindowManager(configuration, rootSurface, /* hostInputTransferToken= */ null) {
+
+ /** Set the view host's touchable region. */
+ fun setTouchRegion(viewHost: SurfaceControlViewHost, region: Region?) {
+ setTouchRegion(viewHost.windowToken.asBinder(), region)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index 9db69d5c1bc5..d72da3a08de5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -31,17 +31,23 @@ import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
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.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
/** Manages tiling for each displayId/userId independently. */
class DesktopTilingDecorViewModel(
private val context: Context,
+ @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
private val displayController: DisplayController,
private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
private val syncQueue: SyncTransactionQueue,
@@ -51,6 +57,7 @@ class DesktopTilingDecorViewModel(
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopModeEventLogger: DesktopModeEventLogger,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -74,8 +81,11 @@ class DesktopTilingDecorViewModel(
val newHandler =
DesktopTilingWindowDecoration(
context,
+ mainDispatcher,
+ bgScope,
syncQueue,
displayController,
+ taskResourceLoader,
displayId,
rootTdaOrganizer,
transitions,
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 7ceac52dd2a1..6f2323347468 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
@@ -20,11 +20,9 @@ import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
-import android.graphics.Bitmap
import android.graphics.Rect
import android.os.IBinder
import android.os.UserHandle
-import android.util.Slog
import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
@@ -37,8 +35,6 @@ import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import com.android.internal.annotations.VisibleForTesting
import com.android.launcher3.icons.BaseIconFactory
-import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
-import com.android.launcher3.icons.IconProvider
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
@@ -47,11 +43,12 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
-import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -60,13 +57,19 @@ import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEvent
import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
import com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge.NONE
import com.android.wm.shell.windowdecor.ResizeVeil
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.extension.isFullscreen
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
class DesktopTilingWindowDecoration(
private var context: Context,
+ @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
private val syncQueue: SyncTransactionQueue,
private val displayController: DisplayController,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
private val displayId: Int,
private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
private val transitions: Transitions,
@@ -110,6 +113,9 @@ class DesktopTilingWindowDecoration(
context,
destinationBounds,
displayController,
+ taskResourceLoader,
+ mainDispatcher,
+ bgScope,
transactionSupplier,
)
val isFirstTiledApp = leftTaskResizingHelper == null && rightTaskResizingHelper == null
@@ -408,11 +414,13 @@ class DesktopTilingWindowDecoration(
val context: Context,
val bounds: Rect,
val displayController: DisplayController,
+ private val taskResourceLoader: WindowDecorTaskResourceLoader,
+ @ShellMainThread val mainDispatcher: MainCoroutineDispatcher,
+ @ShellBackgroundThread val bgScope: CoroutineScope,
val transactionSupplier: Supplier<Transaction>,
) {
var isInitialised = false
var newBounds = Rect(bounds)
- private lateinit var resizeVeilBitmap: Bitmap
private lateinit var resizeVeil: ResizeVeil
private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
private val userContext =
@@ -426,26 +434,14 @@ class DesktopTilingWindowDecoration(
}
private fun initVeil() {
- val baseActivity = taskInfo.baseActivity
- if (baseActivity == null) {
- Slog.e(TAG, "Base activity component not found in task")
- return
- }
- val resizeVeilIconFactory =
- displayContext?.let {
- createIconFactory(displayContext, R.dimen.desktop_mode_resize_veil_icon_size)
- } ?: return
- val pm = userContext.getPackageManager()
- val activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */)
- val provider = IconProvider(displayContext)
- val appIconDrawable = provider.getIcon(activityInfo)
- resizeVeilBitmap =
- resizeVeilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+ displayContext ?: return
resizeVeil =
ResizeVeil(
context = displayContext,
displayController = displayController,
- appIcon = resizeVeilBitmap,
+ taskResourceLoader = taskResourceLoader,
+ mainDispatcher = mainDispatcher,
+ bgScope = bgScope,
parentSurface = desktopModeWindowDecoration.getLeash(),
surfaceControlTransactionSupplier = transactionSupplier,
taskInfo = taskInfo,
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 d94391820d05..dc4fa3788778 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
@@ -40,12 +40,12 @@ import androidx.compose.ui.graphics.toArgb
import androidx.core.content.withStyledAttributes
import androidx.core.view.isGone
import androidx.core.view.isVisible
-import com.android.internal.R.attr.materialColorOnSecondaryContainer
-import com.android.internal.R.attr.materialColorOnSurface
-import com.android.internal.R.attr.materialColorSecondaryContainer
-import com.android.internal.R.attr.materialColorSurfaceContainerHigh
-import com.android.internal.R.attr.materialColorSurfaceContainerLow
-import com.android.internal.R.attr.materialColorSurfaceDim
+import com.android.internal.R.color.materialColorOnSecondaryContainer
+import com.android.internal.R.color.materialColorOnSurface
+import com.android.internal.R.color.materialColorSecondaryContainer
+import com.android.internal.R.color.materialColorSurfaceContainerHigh
+import com.android.internal.R.color.materialColorSurfaceContainerLow
+import com.android.internal.R.color.materialColorSurfaceDim
import com.android.window.flags.Flags
import com.android.window.flags.Flags.enableMinimizeButton
import com.android.wm.shell.R
@@ -72,14 +72,12 @@ class AppHeaderViewHolder(
onCaptionButtonClickListener: View.OnClickListener,
private val onLongClickListener: OnLongClickListener,
onCaptionGenericMotionListener: View.OnGenericMotionListener,
- appName: CharSequence,
- appIconBitmap: Bitmap,
onMaximizeHoverAnimationFinishedListener: () -> Unit
) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
data class HeaderData(
val taskInfo: RunningTaskInfo,
- val isRequestingImmersive: Boolean,
+ val isTaskMaximized: Boolean,
val inFullImmersiveState: Boolean,
val hasGlobalFocus: Boolean,
val enableMaximizeLongClick: Boolean,
@@ -154,8 +152,6 @@ class AppHeaderViewHolder(
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
minimizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
minimizeWindowButton.setOnTouchListener(onCaptionTouchListener)
- appNameTextView.text = appName
- appIconImageView.setImageBitmap(appIconBitmap)
maximizeButtonView.onHoverAnimationFinishedListener =
onMaximizeHoverAnimationFinishedListener
}
@@ -163,16 +159,26 @@ class AppHeaderViewHolder(
override fun bindData(data: HeaderData) {
bindData(
data.taskInfo,
- data.isRequestingImmersive,
+ data.isTaskMaximized,
data.inFullImmersiveState,
data.hasGlobalFocus,
data.enableMaximizeLongClick
)
}
+ /** Sets the app's name in the header. */
+ fun setAppName(name: CharSequence) {
+ appNameTextView.text = name
+ }
+
+ /** Sets the app's icon in the header. */
+ fun setAppIcon(icon: Bitmap) {
+ appIconImageView.setImageBitmap(icon)
+ }
+
private fun bindData(
taskInfo: RunningTaskInfo,
- isRequestingImmersive: Boolean,
+ isTaskMaximized: Boolean,
inFullImmersiveState: Boolean,
hasGlobalFocus: Boolean,
enableMaximizeLongClick: Boolean,
@@ -180,7 +186,7 @@ class AppHeaderViewHolder(
if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
bindDataWithThemedHeaders(
taskInfo,
- isRequestingImmersive,
+ isTaskMaximized,
inFullImmersiveState,
hasGlobalFocus,
enableMaximizeLongClick,
@@ -225,7 +231,7 @@ class AppHeaderViewHolder(
private fun bindDataWithThemedHeaders(
taskInfo: RunningTaskInfo,
- requestingImmersive: Boolean,
+ isTaskMaximized: Boolean,
inFullImmersiveState: Boolean,
hasGlobalFocus: Boolean,
enableMaximizeLongClick: Boolean,
@@ -283,7 +289,7 @@ class AppHeaderViewHolder(
drawableInsets = maximizeDrawableInsets
)
)
- setIcon(getMaximizeButtonIcon(requestingImmersive, inFullImmersiveState))
+ setIcon(getMaximizeButtonIcon(isTaskMaximized, inFullImmersiveState))
}
// Close button.
closeWindowButton.apply {
@@ -358,34 +364,19 @@ class AppHeaderViewHolder(
@DrawableRes
private fun getMaximizeButtonIcon(
- requestingImmersive: Boolean,
+ isTaskMaximized: Boolean,
inFullImmersiveState: Boolean
): Int = when {
- shouldShowEnterFullImmersiveIcon(requestingImmersive, inFullImmersiveState) -> {
- R.drawable.decor_desktop_mode_immersive_button_dark
- }
- shouldShowExitFullImmersiveIcon(requestingImmersive, inFullImmersiveState) -> {
- R.drawable.decor_desktop_mode_immersive_exit_button_dark
+ shouldShowExitFullImmersiveOrMaximizeIcon(isTaskMaximized, inFullImmersiveState) -> {
+ R.drawable.decor_desktop_mode_immersive_or_maximize_exit_button_dark
}
else -> R.drawable.decor_desktop_mode_maximize_button_dark
}
- private fun shouldShowEnterFullImmersiveIcon(
- requestingImmersive: Boolean,
+ private fun shouldShowExitFullImmersiveOrMaximizeIcon(
+ isTaskMaximized: Boolean,
inFullImmersiveState: Boolean
- ): Boolean = Flags.enableFullyImmersiveInDesktop()
- && requestingImmersive && !inFullImmersiveState
-
- private fun shouldShowExitFullImmersiveIcon(
- requestingImmersive: Boolean,
- inFullImmersiveState: Boolean
- ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState)
-
- private fun isInFullImmersiveStateAndRequesting(
- requestingImmersive: Boolean,
- inFullImmersiveState: Boolean
- ): Boolean = Flags.enableFullyImmersiveInDesktop()
- && requestingImmersive && inFullImmersiveState
+ ): Boolean = (Flags.enableFullyImmersiveInDesktop() && inFullImmersiveState) || isTaskMaximized
private fun getHeaderStyle(header: Header): HeaderStyle {
return HeaderStyle(
@@ -595,33 +586,31 @@ class AppHeaderViewHolder(
@ColorInt
private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean): Int {
- val materialColorAttr = when {
+ val materialColor = context.getColor(when {
taskInfo.isTransparentCaptionBarAppearance &&
taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer
taskInfo.isTransparentCaptionBarAppearance &&
!taskInfo.isLightCaptionBarAppearance -> materialColorOnSurface
isDarkMode() -> materialColorOnSurface
else -> materialColorOnSecondaryContainer
- }
+ })
val appDetailsOpacity = when {
isDarkMode() && !hasGlobalFocus -> DARK_THEME_UNFOCUSED_OPACITY
!isDarkMode() && !hasGlobalFocus -> LIGHT_THEME_UNFOCUSED_OPACITY
else -> FOCUSED_OPACITY
}
- context.withStyledAttributes(null, intArrayOf(materialColorAttr), 0, 0) {
- val color = getColor(0, 0)
- return if (appDetailsOpacity == FOCUSED_OPACITY) {
- color
- } else {
- Color.argb(
- appDetailsOpacity,
- Color.red(color),
- Color.green(color),
- Color.blue(color)
- )
- }
+
+
+ return if (appDetailsOpacity == FOCUSED_OPACITY) {
+ materialColor
+ } else {
+ Color.argb(
+ appDetailsOpacity,
+ Color.red(materialColor),
+ Color.green(materialColor),
+ Color.blue(materialColor)
+ )
}
- return 0
}
private fun isDarkMode(): Boolean {
@@ -645,8 +634,6 @@ class AppHeaderViewHolder(
onCaptionButtonClickListener: View.OnClickListener,
onLongClickListener: OnLongClickListener,
onCaptionGenericMotionListener: View.OnGenericMotionListener,
- appName: CharSequence,
- appIconBitmap: Bitmap,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
): AppHeaderViewHolder = AppHeaderViewHolder(
rootView,
@@ -654,8 +641,6 @@ class AppHeaderViewHolder(
onCaptionButtonClickListener,
onLongClickListener,
onCaptionGenericMotionListener,
- appName,
- appIconBitmap,
onMaximizeHoverAnimationFinishedListener,
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 310c2d725c09..ec3fe95f7bef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -54,6 +54,7 @@ public final class TestRunningTaskInfoBuilder {
private final Point mPositionInParent = new Point();
private boolean mIsVisible = false;
private boolean mIsTopActivityTransparent = false;
+ private boolean mIsActivityStackTransparent = false;
private int mNumActivities = 1;
private long mLastActiveTime;
@@ -158,6 +159,12 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setActivityStackTransparent(
+ boolean isActivityStackTransparent) {
+ mIsActivityStackTransparent = isActivityStackTransparent;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setNumActivities(int numActivities) {
mNumActivities = numActivities;
return this;
@@ -187,6 +194,7 @@ public final class TestRunningTaskInfoBuilder {
info.positionInParent = mPositionInParent;
info.isVisible = mIsVisible;
info.isTopActivityTransparent = mIsTopActivityTransparent;
+ info.isActivityStackTransparent = mIsActivityStackTransparent;
info.numActivities = mNumActivities;
info.lastActiveTime = mLastActiveTime;
info.userId = mUserId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f22e2a591df3..47ee7bb20199 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -33,6 +33,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -634,7 +636,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
releaseBackGesture();
mShellExecutor.flushAll();
- verify(mAppCallback).setHandoffHandler(any());
+ verify(mAppCallback).setHandoffHandler(notNull());
}
@Test
@@ -654,7 +656,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
releaseBackGesture();
mShellExecutor.flushAll();
- verify(mAppCallback, never()).setHandoffHandler(any());
+ verify(mAppCallback).setHandoffHandler(isNull());
}
@Test
@@ -716,7 +718,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
callback = mock(Transitions.TransitionFinishCallback.class);
mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
- verify(mBackTransitionHandler).handlePrepareTransition(
+ verify(mBackTransitionHandler).handlePrepareTransition(eq(mockBinder),
eq(tInfo), eq(st), eq(ft), eq(callback));
mBackTransitionHandler.mCloseTransitionRequested = true;
TransitionInfo tInfo2 = createTransitionInfo(TRANSIT_CLOSE, close);
@@ -748,7 +750,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
null /* remoteTransition */);
mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
- verify(mBackTransitionHandler).handlePrepareTransition(
+ verify(mBackTransitionHandler).handlePrepareTransition(eq(mockBinder),
eq(tInfo), eq(st), eq(ft), eq(callback));
mBackTransitionHandler.onAnimationFinished();
@@ -799,7 +801,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
canHandle = mBackTransitionHandler.startAnimation(mockBinder,
prepareInfo, st, ft, callback2);
assertTrue("Handle prepare transition" , canHandle);
- verify(mBackTransitionHandler).handlePrepareTransition(
+ verify(mBackTransitionHandler).handlePrepareTransition(eq(mockBinder),
eq(prepareInfo), eq(st), eq(ft), eq(callback2));
final TransitionInfo closeInfo = createTransitionInfo(TRANSIT_CLOSE, close);
Transitions.TransitionFinishCallback mergeCallback =
@@ -817,7 +819,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
canHandle = mBackTransitionHandler.startAnimation(
mockBinder, prepareInfo, st, ft, callback3);
assertTrue("Handle prepare transition" , canHandle);
- verify(mBackTransitionHandler).handlePrepareTransition(
+ verify(mBackTransitionHandler).handlePrepareTransition(eq(mockBinder),
eq(prepareInfo), eq(st), eq(ft), eq(callback3));
final TransitionInfo.Change open2 = createAppChange(
openTaskId2, TRANSIT_OPEN, FLAG_MOVED_TO_TOP);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
index 329a10998f23..bf03834c70d8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
@@ -15,9 +15,12 @@
*/
package com.android.wm.shell.bubbles.bar;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -61,4 +64,18 @@ public class BubbleBarHandleViewTest extends ShellTestCase {
assertEquals(handleColor,
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
}
+
+ @Test
+ public void testSetHandleInitialColor_beforeUpdateHandleColor_updatesColor() {
+ mHandleView.setHandleInitialColor(Color.RED);
+ assertThat(mHandleView.getHandleColor()).isEqualTo(Color.RED);
+ }
+
+ @Test
+ public void testSetHandleInitialColor_afterUpdateHandleColor_doesNotUpdateColor() {
+ mHandleView.updateHandleColor(/* isRegionDark= */ true, /* animated= */ false);
+ mHandleView.setHandleInitialColor(Color.RED);
+ assertThat(mHandleView.getHandleColor()).isEqualTo(
+ ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt
new file mode 100644
index 000000000000..799b48c2504f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.common
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import java.util.function.BiConsumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.MockitoSession
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for HandlerExecutor.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:HandlerExecutorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class HandlerExecutorTest : ShellTestCase() {
+
+ class TestSetThreadPriorityFn : BiConsumer<Int, Int> {
+ var lastSetPriority = UNSET_THREAD_PRIORITY
+ private set
+ var callCount = 0
+ private set
+
+ override fun accept(tid: Int, priority: Int) {
+ lastSetPriority = priority
+ callCount++
+ }
+
+ fun reset() {
+ lastSetPriority = UNSET_THREAD_PRIORITY
+ callCount = 0
+ }
+ }
+
+ val testSetPriorityFn = TestSetThreadPriorityFn()
+
+ @Test
+ fun defaultExecutorDisallowBoost() {
+ val executor = createTestHandlerExecutor()
+
+ executor.setBoost()
+
+ assertThat(executor.isBoosted()).isFalse()
+ }
+
+ @Test
+ fun boostExecutor_resetWhenNotSet_expectNoOp() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Try to reset and ensure we never try to set the thread priority
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.callCount).isEqualTo(0)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ @Test
+ fun boostExecutor_setResetBoost_expectThreadPriorityUpdated() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Boost and ensure the boosted thread priority is requested
+ executor.setBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset and ensure the default thread priority is requested
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(DEFAULT_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(2)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ @Test
+ fun boostExecutor_overlappingBoost_expectResetOnlyWhenNotOverlapping() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Set and ensure we only update the thread priority once
+ executor.setBoost()
+ executor.setBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset and ensure we are still boosted and the thread priority doesn't change
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset again and ensure we update the thread priority accordingly
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(DEFAULT_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(2)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ /**
+ * Creates a test handler executor backed by a mocked handler thread.
+ */
+ private fun createTestHandlerExecutor(
+ defaultThreadPriority: Int = DEFAULT_THREAD_PRIORITY,
+ boostedThreadPriority: Int = DEFAULT_THREAD_PRIORITY
+ ) : HandlerExecutor {
+ val handler = mock(Handler::class.java)
+ val looper = mock(Looper::class.java)
+ val thread = mock(HandlerThread::class.java)
+ whenever(handler.looper).thenReturn(looper)
+ whenever(looper.thread).thenReturn(thread)
+ whenever(thread.threadId).thenReturn(1234)
+ val executor = HandlerExecutor(handler, defaultThreadPriority, boostedThreadPriority)
+ executor.replaceSetThreadPriorityFn(testSetPriorityFn)
+ return executor
+ }
+
+ companion object {
+ private const val UNSET_THREAD_PRIORITY = 0
+ private const val DEFAULT_THREAD_PRIORITY = 1
+ private const val BOOSTED_THREAD_PRIORITY = 1000
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index b9490b881d08..e92e243172f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -78,7 +78,8 @@ public class PipAppOpsListenerTest {
@Test
public void onActivityPinned_registerAppOpsListener() {
String packageName = "com.android.test.pip";
- mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+ mPipAppOpsListener.setCallback(mMockCallback);
mPipAppOpsListener.onActivityPinned(packageName);
@@ -89,7 +90,8 @@ public class PipAppOpsListenerTest {
@Test
public void onActivityUnpinned_unregisterAppOpsListener() {
- mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+ mPipAppOpsListener.setCallback(mMockCallback);
mPipAppOpsListener.onActivityUnpinned();
@@ -99,7 +101,8 @@ public class PipAppOpsListenerTest {
@Test
public void disablePipAppOps_dismissPip() throws PackageManager.NameNotFoundException {
String packageName = "com.android.test.pip";
- mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+ mPipAppOpsListener.setCallback(mMockCallback);
// Set up the top pip activity info as mTopPipActivity
mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
@@ -131,7 +134,8 @@ public class PipAppOpsListenerTest {
public void disablePipAppOps_differentPackage_doNothing()
throws PackageManager.NameNotFoundException {
String packageName = "com.android.test.pip";
- mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+ mPipAppOpsListener.setCallback(mMockCallback);
// Set up the top pip activity info as mTopPipActivity
mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
@@ -160,7 +164,8 @@ public class PipAppOpsListenerTest {
public void disablePipAppOps_nameNotFound_unregisterAppOpsListener()
throws PackageManager.NameNotFoundException {
String packageName = "com.android.test.pip";
- mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+ mPipAppOpsListener.setCallback(mMockCallback);
// Set up the top pip activity info as mTopPipActivity
mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index 1d390007d470..3ab7b3418e02 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -20,7 +20,6 @@ import android.content.ComponentName
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.R
-import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -35,37 +34,48 @@ import org.junit.runner.RunWith
*/
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class AppCompatUtilsTest : ShellTestCase() {
+class AppCompatUtilsTest : CompatUIShellTestCase() {
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent() {
+ fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
- numActivities = 1
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
+ numActivities = 1
}))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_multipleActivities() {
+ fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
- numActivities = 2
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
+ numActivities = 0
}))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_notDisplayed() {
+ fun testIsTopActivityExemptFromDesktopWindowing_nonTransparentActivitiesInStack() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = false
+ isTopActivityNoDisplay = false
numActivities = 1
+ }))
+ }
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing_transparentActivityStack_notDisplayed() {
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
isTopActivityNoDisplay = true
+ numActivities = 1
}))
}
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 67573dabf2ec..ecf766db0be3 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
@@ -39,9 +39,6 @@ import android.content.res.Configuration;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -52,7 +49,6 @@ 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.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -70,11 +66,8 @@ import com.android.wm.shell.transition.Transitions;
import dagger.Lazy;
-import java.util.Optional;
-
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -82,25 +75,20 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link CompatUIController}.
*
* Build/Install/Run:
- * atest WMShellUnitTests:CompatUIControllerTest
+ * atest WMShellUnitTests:CompatUIControllerTest
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class CompatUIControllerTest extends ShellTestCase {
+public class CompatUIControllerTest extends CompatUIShellTestCase {
private static final int DISPLAY_ID = 0;
private static final int TASK_ID = 12;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private CompatUIController mController;
private ShellInit mShellInit;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index e5d1919decf4..2117b062bf57 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -26,8 +26,6 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.LayoutInflater;
@@ -40,7 +38,6 @@ import androidx.test.filters.SmallTest;
import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
@@ -49,7 +46,6 @@ import com.android.wm.shell.compatui.api.CompatUIEvent;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -66,14 +62,10 @@ import java.util.function.Consumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class CompatUILayoutTest extends ShellTestCase {
+public class CompatUILayoutTest extends CompatUIShellTestCase {
private static final int TASK_ID = 1;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private Consumer<CompatUIEvent> mCallback;
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
new file mode 100644
index 000000000000..5a49d01f2991
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
@@ -0,0 +1,39 @@
+/*
+ * 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.compatui;
+
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Rule;
+
+/**
+ * Base class for CompatUI tests.
+ */
+public class CompatUIShellTestCase extends ShellTestCase {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
index 8fd7c0ec3099..0b37648faeec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
@@ -27,8 +27,6 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.ShellTestCase;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,7 +42,7 @@ import java.util.function.IntSupplier;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class CompatUIStatusManagerTest extends ShellTestCase {
+public class CompatUIStatusManagerTest extends CompatUIShellTestCase {
private FakeCompatUIStatusManagerTest mTestState;
private CompatUIStatusManager mStatusManager;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 1c0175603df9..61b6d803c8be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.compatui;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
@@ -40,9 +39,6 @@ import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.DisplayInfo;
@@ -56,7 +52,6 @@ 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.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
@@ -65,7 +60,6 @@ import com.android.wm.shell.compatui.api.CompatUIEvent;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -82,13 +76,7 @@ import java.util.function.Consumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class CompatUIWindowManagerTest extends ShellTestCase {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+public class CompatUIWindowManagerTest extends CompatUIShellTestCase {
private static final int TASK_ID = 1;
private static final int TASK_WIDTH = 2000;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
index e8191db13084..e786fef1855c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
@@ -25,8 +25,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,10 +32,8 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -51,7 +47,7 @@ import org.mockito.MockitoAnnotations;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class LetterboxEduDialogLayoutTest extends ShellTestCase {
+public class LetterboxEduDialogLayoutTest extends CompatUIShellTestCase {
@Mock
private Runnable mDismissCallback;
@@ -60,10 +56,6 @@ public class LetterboxEduDialogLayoutTest extends ShellTestCase {
private View mDismissButton;
private View mDialogContainer;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
index 94dbd112bb75..09fc082a63e3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.compatui;
import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.window.flags.Flags.FLAG_APP_COMPAT_ASYNC_RELAYOUT;
import static com.android.window.flags.Flags.FLAG_APP_COMPAT_UI_FRAMEWORK;
import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_HIDDEN;
import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_VISIBLE;
@@ -42,10 +43,9 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.DisplayCutout;
@@ -62,7 +62,6 @@ import androidx.test.filters.SmallTest;
import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
@@ -72,7 +71,6 @@ import com.android.wm.shell.transition.Transitions;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -92,7 +90,7 @@ import java.util.function.Consumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class LetterboxEduWindowManagerTest extends ShellTestCase {
+public class LetterboxEduWindowManagerTest extends CompatUIShellTestCase {
private static final int USER_ID_1 = 1;
private static final int USER_ID_2 = 2;
@@ -130,10 +128,6 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
private FakeCompatUIStatusManagerTest mCompatUIStatus;
private CompatUIStatusManager mCompatUIStatusManager;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -317,6 +311,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @DisableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
public void testUpdateCompatInfo_updatesLayoutCorrectly() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
@@ -346,6 +341,36 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
+ public void testUpdateCompatInfo_updatesLayoutCorrectlyAsync() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ assertTrue(windowManager.updateCompatInfo(
+ createTaskInfo(/* eligible= */ true, USER_ID_1, new Rect(50, 25, 150, 75)),
+ mTaskListener, /* canShow= */ true));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
+ /* expectedHeight= */ 50, /* expectedExtraTopMargin= */ 0,
+ /* expectedExtraBottomMargin= */ 0);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture(), any());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+
+ // Window manager should be released (without animation) when eligible becomes false.
+ assertFalse(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ false),
+ mTaskListener, /* canShow= */ true));
+
+ verify(windowManager).release();
+ verify(mOnDismissCallback, never()).accept(any());
+ verify(mAnimationController, never()).startExitAnimation(any(), any());
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testUpdateCompatInfo_notEligibleUntilUpdate_createsLayoutAfterUpdate() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
@@ -375,6 +400,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @DisableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
public void testUpdateDisplayLayout_updatesLayoutCorrectly() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
@@ -397,6 +423,29 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
+ public void testUpdateDisplayLayout_updatesLayoutCorrectlyAsync() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ int newDisplayCutoutTop = DISPLAY_CUTOUT_TOP + 7;
+ int newDisplayCutoutBottom = DISPLAY_CUTOUT_BOTTOM + 9;
+ windowManager.updateDisplayLayout(createDisplayLayout(
+ Insets.of(DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutTop,
+ DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutBottom)));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ TASK_WIDTH,
+ /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */
+ newDisplayCutoutTop, /* expectedExtraBottomMargin= */ newDisplayCutoutBottom);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture(), any());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testRelease_animationIsCancelled() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS
index 5b05af9b0a74..3a017f38f3ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS
@@ -1,2 +1,8 @@
# Bug component: 970984
-# includes OWNERS from parent directories \ No newline at end of file
+# includes OWNERS from parent directories
+
+mariiasand@google.com
+mcarli@google.com
+minagranic@google.com
+gracielawputri@google.com
+eevlachavas@google.com \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
index 0da14d673732..02c099b3cfb2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
@@ -26,8 +26,6 @@ import static org.mockito.Mockito.verify;
import android.app.TaskInfo;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
@@ -36,10 +34,8 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,7 +50,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class ReachabilityEduLayoutTest extends ShellTestCase {
+public class ReachabilityEduLayoutTest extends CompatUIShellTestCase {
private ReachabilityEduLayout mLayout;
private View mMoveUpButton;
@@ -68,10 +64,6 @@ public class ReachabilityEduLayoutTest extends ShellTestCase {
@Mock
private TaskInfo mTaskInfo;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
index eafb41470cda..fa04e070250e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
@@ -25,14 +25,11 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -40,7 +37,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -56,7 +52,7 @@ import java.util.function.BiConsumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class ReachabilityEduWindowManagerTest extends ShellTestCase {
+public class ReachabilityEduWindowManagerTest extends CompatUIShellTestCase {
@Mock
private SyncTransactionQueue mSyncTransactionQueue;
@Mock
@@ -71,10 +67,6 @@ public class ReachabilityEduWindowManagerTest extends ShellTestCase {
private TaskInfo mTaskInfo;
private ReachabilityEduWindowManager mWindowManager;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
index 6b0c5dd2e1c7..2cded9d9776c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
@@ -26,8 +26,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
@@ -36,10 +34,8 @@ import android.widget.CheckBox;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -55,7 +51,7 @@ import java.util.function.Consumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class RestartDialogLayoutTest extends ShellTestCase {
+public class RestartDialogLayoutTest extends CompatUIShellTestCase {
@Mock private Runnable mDismissCallback;
@Mock private Consumer<Boolean> mRestartCallback;
@@ -66,10 +62,6 @@ public class RestartDialogLayoutTest extends ShellTestCase {
private View mDialogContainer;
private CheckBox mDontRepeatCheckBox;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
index cfeef90cb4b6..ebd0f412a0a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
@@ -22,15 +22,12 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.transition.Transitions;
@@ -38,7 +35,6 @@ import com.android.wm.shell.transition.Transitions;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,7 +50,7 @@ import java.util.function.Consumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class RestartDialogWindowManagerTest extends ShellTestCase {
+public class RestartDialogWindowManagerTest extends CompatUIShellTestCase {
@Mock
private SyncTransactionQueue mSyncTransactionQueue;
@@ -66,10 +62,6 @@ public class RestartDialogWindowManagerTest extends ShellTestCase {
private RestartDialogWindowManager mWindowManager;
private TaskInfo mTaskInfo;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index e8e68bdbd940..c6532e13f3cc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -27,8 +27,6 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.LayoutInflater;
@@ -40,7 +38,6 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -48,7 +45,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -66,7 +62,7 @@ import java.util.function.BiConsumer;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class UserAspectRatioSettingsLayoutTest extends ShellTestCase {
+public class UserAspectRatioSettingsLayoutTest extends CompatUIShellTestCase {
private static final int TASK_ID = 1;
@@ -88,10 +84,6 @@ public class UserAspectRatioSettingsLayoutTest extends ShellTestCase {
private UserAspectRatioSettingsLayout mLayout;
private TaskInfo mTaskInfo;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 9f86d49b52c4..096e900199ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -41,8 +41,6 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.Pair;
@@ -56,7 +54,6 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -65,7 +62,6 @@ import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import junit.framework.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -87,7 +83,7 @@ import java.util.function.Supplier;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
+public class UserAspectRatioSettingsWindowManagerTest extends CompatUIShellTestCase {
private static final int TASK_ID = 1;
@@ -112,10 +108,6 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
private TestShellExecutor mExecutor;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
index 75025d9064d3..1399600d5ab9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
@@ -26,6 +26,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.internal.R
import com.android.wm.shell.ShellTestCase
import java.util.function.Consumer
+import kotlin.test.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
@@ -51,6 +52,14 @@ class LetterboxConfigurationTest : ShellTestCase() {
val COLOR_WHITE_RESOURCE_ID = android.R.color.white
@JvmStatic
val COLOR_BLACK_RESOURCE_ID = android.R.color.black
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_DEFAULT = 32
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_VALID = 16
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_NONE = 0
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_INVALID = -10
}
@Test
@@ -112,6 +121,68 @@ class LetterboxConfigurationTest : ShellTestCase() {
}
}
+ @Test
+ fun `default rounded corner radius is used if override is not set`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ }
+ }
+
+ @Test
+ fun `new rounded corner radius is used after setting a valid value`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ }
+ }
+
+ @Test
+ fun `no rounded corner radius is used after setting an invalid value`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_INVALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_NONE)
+ }
+ }
+
+ @Test
+ fun `has rounded corners for different values`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkIsLetterboxActivityCornersRounded(true)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_INVALID)
+ r.checkIsLetterboxActivityCornersRounded(false)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_NONE)
+ r.checkIsLetterboxActivityCornersRounded(false)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkIsLetterboxActivityCornersRounded(true)
+ }
+ }
+
+ @Test
+ fun `reset rounded corners radius`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+
+ r.resetRoundedCornersRadius()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ }
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -135,6 +206,11 @@ class LetterboxConfigurationTest : ShellTestCase() {
.getColor(R.color.config_letterboxBackgroundColor, null)
}
+ fun setDefaultRoundedCornerRadius(radius: Int) {
+ doReturn(radius).`when`(resources)
+ .getInteger(R.integer.config_letterboxActivityCornersRadius)
+ }
+
fun loadConfiguration() {
letterboxConfig = LetterboxConfiguration(ctx)
}
@@ -147,14 +223,30 @@ class LetterboxConfigurationTest : ShellTestCase() {
letterboxConfig.resetLetterboxBackgroundColor()
}
+ fun resetRoundedCornersRadius() {
+ letterboxConfig.resetLetterboxActivityCornersRadius()
+ }
+
fun overrideBackgroundColorId(@ColorRes colorId: Int) {
letterboxConfig.setLetterboxBackgroundColorResourceId(colorId)
}
+ fun overrideRoundedCornersRadius(radius: Int) {
+ letterboxConfig.setLetterboxActivityCornersRadius(radius)
+ }
+
fun checkBackgroundColor(expected: Color) {
val colorComponents = letterboxConfig.getBackgroundColorRgbArray()
val expectedComponents = expected.components
assert(expectedComponents.contentEquals(colorComponents))
}
+
+ fun checkRoundedCornersRadius(expected: Int) {
+ assertEquals(expected, letterboxConfig.getLetterboxActivityCornersRadius())
+ }
+
+ fun checkIsLetterboxActivityCornersRounded(expected: Boolean) {
+ assertEquals(expected, letterboxConfig.isLetterboxActivityCornersRounded())
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
index 95a0c82c76df..88cc981dd30c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
@@ -16,10 +16,8 @@
package com.android.wm.shell.compatui.letterbox
-import android.content.Context
import android.graphics.Rect
import android.view.SurfaceControl
-import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.wm.shell.compatui.letterbox.LetterboxMatchers.asAnyMode
import org.mockito.kotlin.any
import org.mockito.kotlin.clearInvocations
@@ -31,10 +29,7 @@ import org.mockito.kotlin.verify
/**
* Robot to test [LetterboxController] implementations.
*/
-open class LetterboxControllerRobotTest(
- ctx: Context,
- controllerBuilder: (LetterboxSurfaceBuilder) -> LetterboxController
-) {
+abstract class LetterboxControllerRobotTest {
companion object {
@JvmStatic
@@ -44,21 +39,21 @@ open class LetterboxControllerRobotTest(
private val TASK_ID = 20
}
- private val letterboxConfiguration: LetterboxConfiguration
- private val surfaceBuilder: LetterboxSurfaceBuilder
- private val letterboxController: LetterboxController
- private val transaction: SurfaceControl.Transaction
- private val parentLeash: SurfaceControl
+ lateinit var letterboxController: LetterboxController
+ val transaction: SurfaceControl.Transaction
+ val parentLeash: SurfaceControl
init {
- letterboxConfiguration = LetterboxConfiguration(ctx)
- surfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration)
- letterboxController = controllerBuilder(surfaceBuilder)
transaction = getTransactionMock()
parentLeash = mock<SurfaceControl>()
- spyOn(surfaceBuilder)
}
+ fun initController() {
+ letterboxController = buildController()
+ }
+
+ abstract fun buildController(): LetterboxController
+
fun sendCreateSurfaceRequest(
displayId: Int = DISPLAY_ID,
taskId: Int = TASK_ID
@@ -102,16 +97,6 @@ open class LetterboxControllerRobotTest(
letterboxController.dump()
}
- fun checkSurfaceBuilderInvoked(times: Int = 1, name: String = "", callSite: String = "") {
- verify(surfaceBuilder, times(times)).createSurface(
- eq(transaction),
- eq(parentLeash),
- name.asAnyMode(),
- callSite.asAnyMode(),
- any()
- )
- }
-
fun checkTransactionRemovedInvoked(times: Int = 1) {
verify(transaction, times(times)).remove(any())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt
new file mode 100644
index 000000000000..50fdf4510061
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.compatui.letterbox
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.MULTIPLE_SURFACES
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.SINGLE_SURFACE
+import java.util.function.Consumer
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for [LetterboxControllerStrategy].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxControllerStrategyTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxControllerStrategyTest : ShellTestCase() {
+
+ @Test
+ fun `LetterboxMode is MULTIPLE_SURFACES with rounded corners`() {
+ runTestScenario { r ->
+ r.configureRoundedCornerRadius(true)
+ r.configureLetterboxMode()
+ r.checkLetterboxModeIsSingle()
+ }
+ }
+
+ @Test
+ fun `LetterboxMode is MULTIPLE_SURFACES with no rounded corners`() {
+ runTestScenario { r ->
+ r.configureRoundedCornerRadius(false)
+ r.configureLetterboxMode()
+ r.checkLetterboxModeIsMultiple()
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<LetterboxStrategyRobotTest>) {
+ val robot = LetterboxStrategyRobotTest(mContext)
+ consumer.accept(robot)
+ }
+
+ class LetterboxStrategyRobotTest(val ctx: Context) {
+
+ companion object {
+ @JvmStatic
+ private val ROUNDED_CORNERS_TRUE = 10
+ @JvmStatic
+ private val ROUNDED_CORNERS_FALSE = 0
+ }
+
+ private val letterboxConfiguration: LetterboxConfiguration
+ private val letterboxStrategy: LetterboxControllerStrategy
+
+ init {
+ letterboxConfiguration = LetterboxConfiguration(ctx)
+ letterboxStrategy = LetterboxControllerStrategy(letterboxConfiguration)
+ }
+
+ fun configureRoundedCornerRadius(enabled: Boolean) {
+ letterboxConfiguration.setLetterboxActivityCornersRadius(
+ if (enabled) ROUNDED_CORNERS_TRUE else ROUNDED_CORNERS_FALSE
+ )
+ }
+
+ fun configureLetterboxMode() {
+ letterboxStrategy.configureLetterboxMode()
+ }
+
+ fun checkLetterboxModeIsSingle(expected: Boolean = true) {
+ val expectedMode = if (expected) SINGLE_SURFACE else MULTIPLE_SURFACES
+ assertEquals(expectedMode, letterboxStrategy.getLetterboxImplementationMode())
+ }
+
+ fun checkLetterboxModeIsMultiple(expected: Boolean = true) {
+ val expectedMode = if (expected) MULTIPLE_SURFACES else SINGLE_SURFACE
+ assertEquals(expectedMode, letterboxStrategy.getLetterboxImplementationMode())
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
index 06b805233ee7..dd4cb1185b31 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
@@ -16,12 +16,16 @@
package com.android.wm.shell.compatui.letterbox
-import android.content.Context
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import java.util.function.Consumer
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -39,24 +43,14 @@ import org.mockito.kotlin.verify
@SmallTest
class LetterboxUtilsTest : ShellTestCase() {
- val firstLetterboxController = mock<LetterboxController>()
- val secondLetterboxController = mock<LetterboxController>()
- val thirdLetterboxController = mock<LetterboxController>()
-
- private val letterboxControllerBuilder: (LetterboxSurfaceBuilder) -> LetterboxController =
- { _ ->
- firstLetterboxController.append(secondLetterboxController)
- .append(thirdLetterboxController)
- }
-
@Test
fun `Appended LetterboxController invoked creation on all the controllers`() {
runTestScenario { r ->
r.sendCreateSurfaceRequest()
- r.verifyCreateSurfaceInvokedWithRequest(target = firstLetterboxController)
- r.verifyCreateSurfaceInvokedWithRequest(target = secondLetterboxController)
- r.verifyCreateSurfaceInvokedWithRequest(target = thirdLetterboxController)
+ r.verifyCreateSurfaceInvokedWithRequest(target = r.firstLetterboxController)
+ r.verifyCreateSurfaceInvokedWithRequest(target = r.secondLetterboxController)
+ r.verifyCreateSurfaceInvokedWithRequest(target = r.thirdLetterboxController)
}
}
@@ -64,9 +58,9 @@ class LetterboxUtilsTest : ShellTestCase() {
fun `Appended LetterboxController invoked destroy on all the controllers`() {
runTestScenario { r ->
r.sendDestroySurfaceRequest()
- r.verifyDestroySurfaceInvokedWithRequest(target = firstLetterboxController)
- r.verifyDestroySurfaceInvokedWithRequest(target = secondLetterboxController)
- r.verifyDestroySurfaceInvokedWithRequest(target = thirdLetterboxController)
+ r.verifyDestroySurfaceInvokedWithRequest(target = r.firstLetterboxController)
+ r.verifyDestroySurfaceInvokedWithRequest(target = r.secondLetterboxController)
+ r.verifyDestroySurfaceInvokedWithRequest(target = r.thirdLetterboxController)
}
}
@@ -74,9 +68,9 @@ class LetterboxUtilsTest : ShellTestCase() {
fun `Appended LetterboxController invoked update visibility on all the controllers`() {
runTestScenario { r ->
r.sendUpdateSurfaceVisibilityRequest(visible = true)
- r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = firstLetterboxController)
- r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = secondLetterboxController)
- r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = thirdLetterboxController)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = r.firstLetterboxController)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = r.secondLetterboxController)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = r.thirdLetterboxController)
}
}
@@ -84,9 +78,9 @@ class LetterboxUtilsTest : ShellTestCase() {
fun `Appended LetterboxController invoked update bounds on all the controllers`() {
runTestScenario { r ->
r.sendUpdateSurfaceBoundsRequest(taskBounds = Rect(), activityBounds = Rect())
- r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = firstLetterboxController)
- r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = secondLetterboxController)
- r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = thirdLetterboxController)
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = r.firstLetterboxController)
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = r.secondLetterboxController)
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = r.thirdLetterboxController)
}
}
@@ -94,9 +88,38 @@ class LetterboxUtilsTest : ShellTestCase() {
fun `Appended LetterboxController invoked update dump on all the controllers`() {
runTestScenario { r ->
r.invokeDump()
- r.verifyDumpInvoked(target = firstLetterboxController)
- r.verifyDumpInvoked(target = secondLetterboxController)
- r.verifyDumpInvoked(target = thirdLetterboxController)
+ r.verifyDumpInvoked(target = r.firstLetterboxController)
+ r.verifyDumpInvoked(target = r.secondLetterboxController)
+ r.verifyDumpInvoked(target = r.thirdLetterboxController)
+ }
+ }
+
+ @Test
+ fun `runOnItem executes onFound when an item has been found for a key`() {
+ runTestScenario { r ->
+ r.initMap(1 to 2, 3 to 4)
+ r.runOnItem<Int>(1)
+ r.verifyOnItemInvoked(expectedItem = 2)
+ r.verifyOnMissingNotInvoked()
+ }
+ }
+
+ @Test
+ fun `runOnItem executes onMissing when an item has not been found for a key`() {
+ runTestScenario { r ->
+ r.initMap(1 to 2, 3 to 4)
+ r.runOnItem<Int>(8)
+ r.verifyOnItemNotInvoked()
+ r.verifyOnMissingInvoked(expectedKey = 8)
+ }
+ }
+
+ @Test
+ fun `moveAndCrop invoked Move and then Crop`() {
+ runTestScenario { r ->
+ r.invoke(Rect(1, 2, 51, 62))
+ r.verifySetPosition(expectedX = 1f, expectedY = 2f)
+ r.verifySetWindowCrop(expectedWidth = 50, expectedHeight = 60)
}
}
@@ -104,14 +127,21 @@ class LetterboxUtilsTest : ShellTestCase() {
* Runs a test scenario providing a Robot.
*/
fun runTestScenario(consumer: Consumer<AppendLetterboxControllerRobotTest>) {
- val robot = AppendLetterboxControllerRobotTest(mContext, letterboxControllerBuilder)
- consumer.accept(robot)
+ consumer.accept(AppendLetterboxControllerRobotTest().apply { initController() })
}
- class AppendLetterboxControllerRobotTest(
- ctx: Context,
- builder: (LetterboxSurfaceBuilder) -> LetterboxController
- ) : LetterboxControllerRobotTest(ctx, builder) {
+ class AppendLetterboxControllerRobotTest : LetterboxControllerRobotTest() {
+
+ val firstLetterboxController = mock<LetterboxController>()
+ val secondLetterboxController = mock<LetterboxController>()
+ val thirdLetterboxController = mock<LetterboxController>()
+
+ private var testableMap = mutableMapOf<Int, Int>()
+ private var onItemState: Int? = null
+ private var onMissingStateKey: Int? = null
+ private var onMissingStateMap: MutableMap<Int, Int>? = null
+
+ private val surface = SurfaceControl()
fun verifyCreateSurfaceInvokedWithRequest(
target: LetterboxController,
@@ -147,5 +177,50 @@ class LetterboxUtilsTest : ShellTestCase() {
) {
verify(target, times(times)).dump()
}
+
+ fun initMap(vararg values: Pair<Int, Int>) = testableMap.putAll(values.toMap())
+
+ fun <T> runOnItem(key: Int) {
+ testableMap.runOnItem(key, onFound = { item ->
+ onItemState = item
+ }, onMissed = { k, m ->
+ onMissingStateKey = k
+ onMissingStateMap = m
+ })
+ }
+
+ fun verifyOnItemInvoked(expectedItem: Int) {
+ assertEquals(expectedItem, onItemState)
+ }
+
+ fun verifyOnItemNotInvoked() {
+ assertNull(onItemState)
+ }
+
+ fun verifyOnMissingInvoked(expectedKey: Int) {
+ assertEquals(expectedKey, onMissingStateKey)
+ assertEquals(onMissingStateMap, testableMap)
+ }
+
+ fun verifyOnMissingNotInvoked() {
+ assertNull(onMissingStateKey)
+ assertNull(onMissingStateMap)
+ }
+
+ fun invoke(rect: Rect) {
+ transaction.moveAndCrop(surface, rect)
+ }
+
+ fun verifySetPosition(expectedX: Float, expectedY: Float) {
+ verify(transaction).setPosition(surface, expectedX, expectedY)
+ }
+
+ fun verifySetWindowCrop(expectedWidth: Int, expectedHeight: Int) {
+ verify(transaction).setWindowCrop(surface, expectedWidth, expectedHeight)
+ }
+
+ override fun buildController(): LetterboxController =
+ firstLetterboxController.append(secondLetterboxController)
+ .append(thirdLetterboxController)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt
index e6bff4c1ec15..3b72ff1cac71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.compatui.letterbox
-import android.content.Context
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
@@ -64,65 +63,48 @@ class MixedLetterboxControllerTest : ShellTestCase() {
* Runs a test scenario providing a Robot.
*/
fun runTestScenario(consumer: Consumer<MixedLetterboxControllerRobotTest>) {
- val robot = MixedLetterboxControllerRobotTest(mContext, ObjectToTestHolder())
- consumer.accept(robot)
+ consumer.accept(MixedLetterboxControllerRobotTest().apply { initController() })
}
- class MixedLetterboxControllerRobotTest(
- ctx: Context,
- private val objectToTestHolder: ObjectToTestHolder
- ) : LetterboxControllerRobotTest(ctx, objectToTestHolder.controllerBuilder) {
+ class MixedLetterboxControllerRobotTest : LetterboxControllerRobotTest() {
+ val singleLetterboxController: SingleSurfaceLetterboxController =
+ mock<SingleSurfaceLetterboxController>()
+ val multipleLetterboxController: MultiSurfaceLetterboxController =
+ mock<MultiSurfaceLetterboxController>()
+ val controllerStrategy: LetterboxControllerStrategy = mock<LetterboxControllerStrategy>()
fun configureStrategyFor(letterboxMode: LetterboxMode) {
- doReturn(letterboxMode).`when`(objectToTestHolder.controllerStrategy)
- .getLetterboxImplementationMode()
+ doReturn(letterboxMode).`when`(controllerStrategy).getLetterboxImplementationMode()
}
fun checkCreateInvokedOnSingleController(times: Int = 1) {
- verify(
- objectToTestHolder.singleLetterboxController,
- times(times)
- ).createLetterboxSurface(any(), any(), any())
+ verify(singleLetterboxController, times(times)).createLetterboxSurface(
+ any(),
+ any(),
+ any()
+ )
}
fun checkCreateInvokedOnMultiController(times: Int = 1) {
- verify(
- objectToTestHolder.multipleLetterboxController,
- times(times)
- ).createLetterboxSurface(any(), any(), any())
+ verify(multipleLetterboxController, times(times)).createLetterboxSurface(
+ any(),
+ any(),
+ any()
+ )
}
fun checkDestroyInvokedOnSingleController(times: Int = 1) {
- verify(
- objectToTestHolder.singleLetterboxController,
- times(times)
- ).destroyLetterboxSurface(any(), any())
+ verify(singleLetterboxController, times(times)).destroyLetterboxSurface(any(), any())
}
fun checkDestroyInvokedOnMultiController(times: Int = 1) {
- verify(
- objectToTestHolder.multipleLetterboxController,
- times(times)
- ).destroyLetterboxSurface(any(), any())
+ verify(multipleLetterboxController, times(times)).destroyLetterboxSurface(any(), any())
}
- }
-
- data class ObjectToTestHolder(
- val singleLetterboxController: SingleSurfaceLetterboxController =
- mock<SingleSurfaceLetterboxController>(),
- val multipleLetterboxController: MultiSurfaceLetterboxController =
- mock<MultiSurfaceLetterboxController>(),
- val controllerStrategy: LetterboxControllerStrategy = mock<LetterboxControllerStrategy>()
- ) {
-
- private val mixedController =
- MixedLetterboxController(
- singleLetterboxController,
- multipleLetterboxController,
- controllerStrategy
- )
- val controllerBuilder: (LetterboxSurfaceBuilder) -> LetterboxController =
- { _ -> mixedController }
+ override fun buildController(): LetterboxController = MixedLetterboxController(
+ singleLetterboxController,
+ multipleLetterboxController,
+ controllerStrategy
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxControllerTest.kt
index 295d4edf206b..3fd837db478c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxControllerTest.kt
@@ -16,13 +16,20 @@
package com.android.wm.shell.compatui.letterbox
+import android.content.Context
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxMatchers.asAnyMode
import java.util.function.Consumer
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
/**
* Tests for [MultiSurfaceLetterboxController].
@@ -147,9 +154,33 @@ class MultiSurfaceLetterboxControllerTest : ShellTestCase() {
/**
* Runs a test scenario providing a Robot.
*/
- fun runTestScenario(consumer: Consumer<LetterboxControllerRobotTest>) {
- val robot =
- LetterboxControllerRobotTest(mContext, { sb -> MultiSurfaceLetterboxController(sb) })
- consumer.accept(robot)
+ fun runTestScenario(consumer: Consumer<MultiLetterboxControllerRobotTest>) {
+ consumer.accept(MultiLetterboxControllerRobotTest(mContext).apply { initController() })
+ }
+
+ class MultiLetterboxControllerRobotTest(context: Context) :
+ LetterboxControllerRobotTest() {
+
+ private val letterboxConfiguration: LetterboxConfiguration
+ private val surfaceBuilder: LetterboxSurfaceBuilder
+
+ init {
+ letterboxConfiguration = LetterboxConfiguration(context)
+ surfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration)
+ spyOn(surfaceBuilder)
+ }
+
+ override fun buildController(): LetterboxController =
+ MultiSurfaceLetterboxController(surfaceBuilder)
+
+ fun checkSurfaceBuilderInvoked(times: Int = 1, name: String = "", callSite: String = "") {
+ verify(surfaceBuilder, times(times)).createSurface(
+ eq(transaction),
+ eq(parentLeash),
+ name.asAnyMode(),
+ callSite.asAnyMode(),
+ any()
+ )
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxControllerTest.kt
index 125e700bcd42..e6ffe98875ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxControllerTest.kt
@@ -16,13 +16,20 @@
package com.android.wm.shell.compatui.letterbox
+import android.content.Context
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxMatchers.asAnyMode
import java.util.function.Consumer
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
/**
* Tests for [SingleSurfaceLetterboxController].
@@ -120,9 +127,33 @@ class SingleSurfaceLetterboxControllerTest : ShellTestCase() {
/**
* Runs a test scenario providing a Robot.
*/
- fun runTestScenario(consumer: Consumer<LetterboxControllerRobotTest>) {
- val robot =
- LetterboxControllerRobotTest(mContext, { sb -> SingleSurfaceLetterboxController(sb) })
- consumer.accept(robot)
+ fun runTestScenario(consumer: Consumer<SingleLetterboxControllerRobotTest>) {
+ consumer.accept(SingleLetterboxControllerRobotTest(mContext).apply { initController() })
+ }
+
+ class SingleLetterboxControllerRobotTest(context: Context) :
+ LetterboxControllerRobotTest() {
+
+ private val letterboxConfiguration: LetterboxConfiguration
+ private val surfaceBuilder: LetterboxSurfaceBuilder
+
+ init {
+ letterboxConfiguration = LetterboxConfiguration(context)
+ surfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration)
+ spyOn(surfaceBuilder)
+ }
+
+ override fun buildController(): LetterboxController =
+ SingleSurfaceLetterboxController(surfaceBuilder)
+
+ fun checkSurfaceBuilderInvoked(times: Int = 1, name: String = "", callSite: String = "") {
+ verify(surfaceBuilder, times(times)).createSurface(
+ eq(transaction),
+ eq(parentLeash),
+ name.asAnyMode(),
+ callSite.asAnyMode(),
+ any()
+ )
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
index 9b4cc17e19d9..db00f41f723b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
@@ -64,7 +64,7 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
context,
testExecutor,
testExecutor,
- transactionSupplier
+ transactionSupplier,
)
}
@@ -81,11 +81,11 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = WindowManager.TRANSIT_OPEN,
- task = createTask(WINDOWING_MODE_FREEFORM)
+ task = createTask(WINDOWING_MODE_FREEFORM),
),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate open transition", animates)
@@ -99,7 +99,7 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate fullscreen task close transition", animates)
@@ -113,11 +113,11 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
changeMode = WindowManager.TRANSIT_OPEN,
- task = createTask(WINDOWING_MODE_FREEFORM)
+ task = createTask(WINDOWING_MODE_FREEFORM),
),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate opening freeform task close transition", animates)
@@ -131,7 +131,7 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertTrue("Should animate closing freeform task close transition", animates)
@@ -140,7 +140,7 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
private fun createTransitionInfo(
type: Int = WindowManager.TRANSIT_CLOSE,
changeMode: Int = WindowManager.TRANSIT_CLOSE,
- task: RunningTaskInfo
+ task: RunningTaskInfo,
): TransitionInfo =
TransitionInfo(type, 0 /* flags */).apply {
addChange(
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 41a594a3347a..ecad5217b87f 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
@@ -46,6 +46,7 @@ import com.android.wm.shell.desktopmode.persistence.Desktop
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
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.transition.Transitions
import junit.framework.Assert.assertEquals
@@ -98,6 +99,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Mock lateinit var persistentRepository: DesktopPersistentRepository
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var shellController: ShellController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var handler: DesktopActivityOrientationChangeHandler
@@ -123,19 +125,26 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
DesktopUserRepositories(
context,
shellInit,
+ shellController,
persistentRepository,
repositoryInitializer,
testScope,
- userManager
+ userManager,
)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
- Desktop.getDefaultInstance()
- )
+ whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+ .thenReturn(Desktop.getDefaultInstance())
- handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
- taskStackListener, resizeTransitionHandler, userRepositories)
+ handler =
+ DesktopActivityOrientationChangeHandler(
+ context,
+ shellInit,
+ shellTaskOrganizer,
+ taskStackListener,
+ resizeTransitionHandler,
+ userRepositories,
+ )
shellInit.init()
}
@@ -158,19 +167,28 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
clearInvocations(shellInit)
- handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
- taskStackListener, resizeTransitionHandler, userRepositories)
+ handler =
+ DesktopActivityOrientationChangeHandler(
+ context,
+ shellInit,
+ shellTaskOrganizer,
+ taskStackListener,
+ resizeTransitionHandler,
+ userRepositories,
+ )
- verify(shellInit, never()).addInitCallback(any(),
- any<DesktopActivityOrientationChangeHandler>())
+ verify(shellInit, never())
+ .addInitCallback(any(), any<DesktopActivityOrientationChangeHandler>())
}
@Test
fun handleActivityOrientationChange_resizeable_doNothing() {
val task = setUpFreeformTask()
- taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
- SCREEN_ORIENTATION_LANDSCAPE)
+ taskStackListener.onActivityRequestedOrientationChanged(
+ task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ )
verify(resizeTransitionHandler, never()).startTransition(any(), any())
}
@@ -186,8 +204,10 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
userRepositories.current.addTask(DEFAULT_DISPLAY, task.taskId, isVisible = true)
runningTasks.add(task)
- taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
- SCREEN_ORIENTATION_LANDSCAPE)
+ taskStackListener.onActivityRequestedOrientationChanged(
+ task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ )
verify(resizeTransitionHandler, never()).startTransition(any(), any())
}
@@ -195,8 +215,11 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() {
val task = setUpFreeformTask(isResizeable = false)
- val newTask = setUpFreeformTask(isResizeable = false,
- orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT)
+ val newTask =
+ setUpFreeformTask(
+ isResizeable = false,
+ orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT,
+ )
handler.handleActivityOrientationChange(task, newTask)
@@ -208,8 +231,10 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
val task = setUpFreeformTask(isResizeable = false)
userRepositories.current.updateTask(task.displayId, task.taskId, isVisible = false)
- taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
- SCREEN_ORIENTATION_LANDSCAPE)
+ taskStackListener.onActivityRequestedOrientationChanged(
+ task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ )
verify(resizeTransitionHandler, never()).startTransition(any(), any())
}
@@ -218,8 +243,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() {
val task = setUpFreeformTask(isResizeable = false)
val oldBounds = task.configuration.windowConfiguration.bounds
- val newTask = setUpFreeformTask(isResizeable = false,
- orientation = SCREEN_ORIENTATION_LANDSCAPE)
+ val newTask =
+ setUpFreeformTask(isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE)
handler.handleActivityOrientationChange(task, newTask)
@@ -239,9 +264,12 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() {
val oldBounds = Rect(0, 0, 500, 200)
- val task = setUpFreeformTask(
- isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds
- )
+ val task =
+ setUpFreeformTask(
+ isResizeable = false,
+ orientation = SCREEN_ORIENTATION_LANDSCAPE,
+ bounds = oldBounds,
+ )
val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds)
handler.handleActivityOrientationChange(task, newTask)
@@ -263,7 +291,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
displayId: Int = DEFAULT_DISPLAY,
isResizeable: Boolean = true,
orientation: Int = SCREEN_ORIENTATION_PORTRAIT,
- bounds: Rect? = Rect(0, 0, 200, 500)
+ bounds: Rect? = Rect(0, 0, 200, 500),
): RunningTaskInfo {
val task = createFreeformTask(displayId, bounds)
val activityInfo = ActivityInfo()
@@ -288,4 +316,4 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
index 6df8d6fd7717..d14c6402982d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
@@ -56,11 +56,7 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
@Before
fun setUp() {
handler =
- DesktopBackNavigationTransitionHandler(
- testExecutor,
- testExecutor,
- displayController
- )
+ DesktopBackNavigationTransitionHandler(testExecutor, testExecutor, displayController)
whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
}
@@ -75,13 +71,13 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
handler.startAnimation(
transition = mock(),
info =
- createTransitionInfo(
- type = WindowManager.TRANSIT_OPEN,
- task = createTask(WINDOWING_MODE_FREEFORM)
- ),
+ createTransitionInfo(
+ type = WindowManager.TRANSIT_OPEN,
+ task = createTask(WINDOWING_MODE_FREEFORM),
+ ),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate open transition", animates)
@@ -95,7 +91,7 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate fullscreen task to back transition", animates)
@@ -107,13 +103,13 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
handler.startAnimation(
transition = mock(),
info =
- createTransitionInfo(
- changeMode = WindowManager.TRANSIT_OPEN,
- task = createTask(WINDOWING_MODE_FREEFORM)
- ),
+ createTransitionInfo(
+ changeMode = WindowManager.TRANSIT_OPEN,
+ task = createTask(WINDOWING_MODE_FREEFORM),
+ ),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertFalse("Should not animate opening freeform task to back transition", animates)
@@ -127,7 +123,7 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertTrue("Should animate going to back freeform task close transition", animates)
@@ -138,22 +134,24 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
val animates =
handler.startAnimation(
transition = mock(),
- info = createTransitionInfo(
- type = TRANSIT_CLOSE,
- changeMode = TRANSIT_CLOSE,
- task = createTask(WINDOWING_MODE_FREEFORM)
- ),
+ info =
+ createTransitionInfo(
+ type = TRANSIT_CLOSE,
+ changeMode = TRANSIT_CLOSE,
+ task = createTask(WINDOWING_MODE_FREEFORM),
+ ),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
assertTrue("Should animate going to back freeform task close transition", animates)
}
+
private fun createTransitionInfo(
type: Int = WindowManager.TRANSIT_TO_BACK,
changeMode: Int = WindowManager.TRANSIT_TO_BACK,
- task: RunningTaskInfo
+ task: RunningTaskInfo,
): TransitionInfo =
TransitionInfo(type, 0 /* flags */).apply {
addChange(
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 fea82365c1a0..6a3717427e93 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
@@ -63,109 +63,115 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
- @Mock lateinit var testExecutor: ShellExecutor
- @Mock lateinit var transitions: Transitions
- @Mock lateinit var displayController: DisplayController
- @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
- @Mock private lateinit var mockWindowManager: IWindowManager
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var transitions: Transitions
+ @Mock lateinit var displayController: DisplayController
+ @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockWindowManager: IWindowManager
- private lateinit var shellInit: ShellInit
- private lateinit var handler: DesktopDisplayEventHandler
+ private lateinit var shellInit: ShellInit
+ private lateinit var handler: DesktopDisplayEventHandler
- @Before
- fun setUp() {
- shellInit = spy(ShellInit(testExecutor))
- whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
- handler =
- DesktopDisplayEventHandler(
- context,
- shellInit,
- transitions,
- displayController,
- rootTaskDisplayAreaOrganizer,
- mockWindowManager,
- )
- shellInit.init()
- }
+ @Before
+ fun setUp() {
+ shellInit = spy(ShellInit(testExecutor))
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+ val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+ handler =
+ DesktopDisplayEventHandler(
+ context,
+ shellInit,
+ transitions,
+ displayController,
+ rootTaskDisplayAreaOrganizer,
+ mockWindowManager,
+ )
+ shellInit.init()
+ }
- private fun testDisplayWindowingModeSwitch(
- defaultWindowingMode: Int,
- extendedDisplayEnabled: Boolean,
- expectTransition: Boolean
- ) {
- val externalDisplayId = 100
- val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
- verify(displayController).addDisplayWindowListener(captor.capture())
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
- val settingsSession = ExtendedDisplaySettingsSession(
- context.contentResolver, if (extendedDisplayEnabled) 1 else 0)
+ private fun testDisplayWindowingModeSwitch(
+ defaultWindowingMode: Int,
+ extendedDisplayEnabled: Boolean,
+ expectTransition: Boolean,
+ ) {
+ val externalDisplayId = 100
+ val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
+ verify(displayController).addDisplayWindowListener(captor.capture())
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
+ val settingsSession =
+ ExtendedDisplaySettingsSession(
+ context.contentResolver,
+ if (extendedDisplayEnabled) 1 else 0,
+ )
- settingsSession.use {
- // The external display connected.
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
- captor.value.onDisplayAdded(externalDisplayId)
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- // The external display disconnected.
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY))
- captor.value.onDisplayRemoved(externalDisplayId)
+ settingsSession.use {
+ // The external display connected.
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+ captor.value.onDisplayAdded(externalDisplayId)
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ // The external display disconnected.
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ captor.value.onDisplayRemoved(externalDisplayId)
- if (expectTransition) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
- .isEqualTo(defaultWindowingMode)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
+ if (expectTransition) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
+ .isEqualTo(defaultWindowingMode)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
+ }
+ }
}
- }
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = false,
- expectTransition = false
- )
- }
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectTransition = false,
+ )
+ }
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
- expectTransition = true
- )
- }
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectTransition = true,
+ )
+ }
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FREEFORM,
- extendedDisplayEnabled = true,
- expectTransition = false
- )
- }
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectTransition = false,
+ )
+ }
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int
- ) : AutoCloseable {
- private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
- private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+ private class ExtendedDisplaySettingsSession(
+ private val contentResolver: ContentResolver,
+ private val overrideValue: Int,
+ ) : AutoCloseable {
+ private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+ private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
- init { Settings.Global.putInt(contentResolver, settingName, overrideValue) }
+ init {
+ Settings.Global.putInt(contentResolver, settingName, overrideValue)
+ }
- override fun close() {
- Settings.Global.putInt(contentResolver, settingName, initialValue)
+ override fun close() {
+ Settings.Global.putInt(contentResolver, settingName, initialValue)
+ }
}
- }
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index db4c7465ae48..47d133b974e6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -42,6 +42,7 @@ 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.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopImmersiveController.Direction
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitReason.USER_INTERACTION
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.sysui.ShellInit
@@ -87,23 +88,33 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
@Before
fun setUp() {
- userRepositories = DesktopUserRepositories(
- context, ShellInit(TestShellExecutor()), mock(), mock(), mock(), mock()
- )
+ userRepositories =
+ DesktopUserRepositories(
+ context,
+ ShellInit(TestShellExecutor()),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY))
.thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation ->
(invocation.getArgument(0) as Rect).set(STABLE_BOUNDS)
}
- controller = DesktopImmersiveController(
- shellInit = mock(),
- transitions = mockTransitions,
- desktopUserRepositories = userRepositories,
- displayController = mockDisplayController,
- shellTaskOrganizer = mockShellTaskOrganizer,
- shellCommandHandler = mock(),
- transactionSupplier = transactionSupplier,
- )
+ whenever(mockDisplayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
+ whenever(mockDisplayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
+ controller =
+ DesktopImmersiveController(
+ shellInit = mock(),
+ transitions = mockTransitions,
+ desktopUserRepositories = userRepositories,
+ displayController = mockDisplayController,
+ shellTaskOrganizer = mockShellTaskOrganizer,
+ shellCommandHandler = mock(),
+ transactionSupplier = transactionSupplier,
+ )
desktopRepository = userRepositories.current
}
@@ -116,15 +127,13 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
controller.moveTaskToImmersive(task)
controller.onTransitionReady(
transition = mockBinder,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -142,16 +151,14 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull()
controller.moveTaskToImmersive(task)
controller.onTransitionReady(
transition = mockBinder,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -168,15 +175,13 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.onTransitionReady(
transition = mockBinder,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -194,16 +199,14 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.onTransitionReady(
transition = mockBinder,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -217,16 +220,23 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.onTransitionReady(
transition = mock(IBinder::class.java),
- info = createTransitionInfo(
- changes = listOf(createChange(task).apply {
- setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90)
- })
- ),
+ info =
+ createTransitionInfo(
+ changes =
+ listOf(
+ createChange(task).apply {
+ setRotation(
+ /* start= */ Surface.ROTATION_0,
+ /* end= */ Surface.ROTATION_90,
+ )
+ }
+ )
+ ),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -244,8 +254,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
controller.moveTaskToImmersive(task)
controller.moveTaskToImmersive(task)
- verify(mockTransitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
+ verify(mockTransitions, times(1)).startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
}
@Test
@@ -258,8 +267,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
- verify(mockTransitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
+ verify(mockTransitions, times(1)).startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
}
@Test
@@ -272,15 +280,17 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isTrue()
+ assertTransitionPending(
+ transition = transition,
+ taskId = task.taskId,
+ direction = Direction.EXIT,
+ animate = false,
+ )
}
@Test
@@ -293,15 +303,17 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isFalse()
+ assertTransitionNotPending(
+ transition = transition,
+ taskId = task.taskId,
+ direction = Direction.EXIT,
+ animate = false,
+ )
}
@Test
@@ -314,7 +326,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -332,7 +344,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -350,20 +362,26 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
- controller.exitImmersiveIfApplicable(
- wct = wct,
- displayId = DEFAULT_DISPLAY,
- excludeTaskId = task.taskId,
- reason = USER_INTERACTION,
- ).asExit()?.runOnTransitionStart?.invoke(transition)
+ controller
+ .exitImmersiveIfApplicable(
+ wct = wct,
+ displayId = DEFAULT_DISPLAY,
+ excludeTaskId = task.taskId,
+ reason = USER_INTERACTION,
+ )
+ .asExit()
+ ?.runOnTransitionStart
+ ?.invoke(transition)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isFalse()
+ assertTransitionNotPending(
+ transition = transition,
+ taskId = task.taskId,
+ animate = false,
+ direction = Direction.EXIT,
+ )
}
@Test
@@ -375,7 +393,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
@@ -392,7 +410,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
@@ -410,16 +428,21 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
- controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
- .asExit()?.runOnTransitionStart?.invoke(transition)
+ controller
+ .exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
+ .asExit()
+ ?.runOnTransitionStart
+ ?.invoke(transition)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isTrue()
+ assertTransitionPending(
+ transition = transition,
+ taskId = task.taskId,
+ direction = Direction.EXIT,
+ animate = false,
+ )
}
@Test
@@ -431,7 +454,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
val result = controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
@@ -448,11 +471,16 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
- val result = controller.exitImmersiveIfApplicable(
- wct, task.displayId, excludeTaskId = null, USER_INTERACTION)
+ val result =
+ controller.exitImmersiveIfApplicable(
+ wct,
+ task.displayId,
+ excludeTaskId = null,
+ USER_INTERACTION,
+ )
assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
}
@@ -467,24 +495,24 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
controller.onTransitionFinished(transition, aborted = false)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isFalse()
+ assertTransitionNotPending(
+ transition = transition,
+ taskId = task.taskId,
+ direction = Direction.EXIT,
+ animate = false,
+ )
}
@Test
@@ -498,29 +526,31 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
controller.onTransitionMerged(transition, mergedToTransition)
controller.onTransitionFinished(mergedToTransition, aborted = false)
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isFalse()
- assertThat(controller.pendingExternalExitTransitions.any { exit ->
- exit.transition == mergedToTransition && exit.displayId == DEFAULT_DISPLAY
- && exit.taskId == task.taskId
- }).isFalse()
+ assertTransitionNotPending(
+ transition = transition,
+ taskId = task.taskId,
+ animate = false,
+ direction = Direction.EXIT,
+ )
+ assertTransitionNotPending(
+ transition = mergedToTransition,
+ taskId = task.taskId,
+ animate = false,
+ direction = Direction.EXIT,
+ )
}
@Test
@@ -533,15 +563,13 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -552,7 +580,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
- Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+ Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
)
fun onTransitionReady_pendingExit_removesBoundsBeforeImmersive() {
val task = createFreeformTask()
@@ -562,16 +590,14 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
- info = createTransitionInfo(
- changes = listOf(createChange(task))
- ),
+ info = createTransitionInfo(changes = listOf(createChange(task))),
startTransaction = SurfaceControl.Transaction(),
finishTransaction = SurfaceControl.Transaction(),
)
@@ -589,20 +615,21 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
assertThat(
- wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
- ).isTrue()
+ wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
+ )
+ .isTrue()
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
- Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+ Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
)
fun exitImmersiveIfApplicable_preImmersiveBoundsSaved_changesBoundsToPreImmersiveBounds() {
val task = createFreeformTask()
@@ -611,23 +638,21 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
val preImmersiveBounds = Rect(100, 100, 500, 500)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds)
controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
- assertThat(
- wct.hasBoundsChange(task.token, preImmersiveBounds)
- ).isTrue()
+ assertThat(wct.hasBoundsChange(task.token, preImmersiveBounds)).isTrue()
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
- Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+ Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
)
fun exitImmersiveIfApplicable_preImmersiveBoundsNotSaved_changesBoundsToInitialBounds() {
val task = createFreeformTask()
@@ -636,14 +661,13 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
- assertThat(
- wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task))
- ).isTrue()
+ assertThat(wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task)))
+ .isTrue()
}
@Test
@@ -655,10 +679,13 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
- controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
- .asExit()?.runOnTransitionStart?.invoke(Binder())
+ controller
+ .exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
+ .asExit()
+ ?.runOnTransitionStart
+ ?.invoke(Binder())
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
@@ -676,7 +703,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = DEFAULT_DISPLAY,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -686,7 +713,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun externalAnimateResizeChange_doesNotCleanUpPendingTransitionState() {
+ fun externalAnimateResizeChange_doesNotRemovePendingTransition() {
val task = createFreeformTask()
val mockBinder = mock(IBinder::class.java)
whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)))
@@ -694,27 +721,29 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = true
+ immersive = true,
)
controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.animateResizeChange(
- change = TransitionInfo.Change(task.token, SurfaceControl()).apply {
- taskInfo = task
- },
+ change = TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task },
startTransaction = StubTransaction(),
finishTransaction = StubTransaction(),
- finishCallback = { }
+ finishCallback = {},
)
animatorTestRule.advanceTimeBy(DesktopImmersiveController.FULL_IMMERSIVE_ANIM_DURATION_MS)
- assertThat(controller.state).isNotNull()
+ assertTransitionPending(
+ transition = mockBinder,
+ taskId = task.taskId,
+ direction = Direction.EXIT,
+ )
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun startAnimation_missingChange_clearsState() {
+ fun startAnimation_missingChange_removesPendingTransition() {
val task = createFreeformTask()
val mockBinder = mock(IBinder::class.java)
whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)))
@@ -722,7 +751,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
desktopRepository.setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
- immersive = false
+ immersive = false,
)
controller.moveTaskToImmersive(task)
@@ -732,41 +761,81 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
info = createTransitionInfo(changes = emptyList()),
startTransaction = StubTransaction(),
finishTransaction = StubTransaction(),
- finishCallback = {}
+ finishCallback = {},
+ )
+
+ assertTransitionNotPending(
+ transition = mockBinder,
+ taskId = task.taskId,
+ direction = Direction.ENTER,
)
+ }
- assertThat(controller.state).isNull()
+ private fun assertTransitionPending(
+ transition: IBinder,
+ taskId: Int,
+ direction: Direction,
+ animate: Boolean = true,
+ displayId: Int = DEFAULT_DISPLAY,
+ ) {
+ assertThat(
+ controller.pendingImmersiveTransitions.any { pendingTransition ->
+ pendingTransition.transition == transition &&
+ pendingTransition.displayId == displayId &&
+ pendingTransition.taskId == taskId &&
+ pendingTransition.animate == animate &&
+ pendingTransition.direction == direction
+ }
+ )
+ .isTrue()
+ }
+
+ private fun assertTransitionNotPending(
+ transition: IBinder,
+ taskId: Int,
+ direction: Direction,
+ animate: Boolean = true,
+ displayId: Int = DEFAULT_DISPLAY,
+ ) {
+ assertThat(
+ controller.pendingImmersiveTransitions.any { pendingTransition ->
+ pendingTransition.transition == transition &&
+ pendingTransition.displayId == displayId &&
+ pendingTransition.taskId == taskId &&
+ pendingTransition.direction == direction
+ }
+ )
+ .isFalse()
}
private fun createTransitionInfo(
@TransitionType type: Int = TRANSIT_CHANGE,
@TransitionFlags flags: Int = 0,
- changes: List<TransitionInfo.Change> = emptyList()
- ): TransitionInfo = TransitionInfo(type, flags).apply {
- changes.forEach { change -> addChange(change) }
- }
+ changes: List<TransitionInfo.Change> = emptyList(),
+ ): TransitionInfo =
+ TransitionInfo(type, flags).apply { changes.forEach { change -> addChange(change) } }
private fun createChange(task: RunningTaskInfo): TransitionInfo.Change =
- TransitionInfo.Change(task.token, SurfaceControl()).apply {
- taskInfo = task
- }
+ TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
private fun WindowContainerTransaction.hasBoundsChange(token: WindowContainerToken): Boolean =
this.changes.any { change ->
- change.key == token.asBinder()
- && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
+ change.key == token.asBinder() &&
+ (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
}
private fun WindowContainerTransaction.hasBoundsChange(
token: WindowContainerToken,
bounds: Rect,
- ): Boolean = this.changes.any { change ->
- change.key == token.asBinder()
- && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
- && change.value.configuration.windowConfiguration.bounds == bounds
- }
+ ): Boolean =
+ this.changes.any { change ->
+ change.key == token.asBinder() &&
+ (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.value.configuration.windowConfiguration.bounds == bounds
+ }
companion object {
private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900)
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2000, 2000)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 49a7e2951a7e..3cf84d92a625 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -85,34 +85,22 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@JvmField @Rule val setFlagsRule = SetFlagsRule()
- @Mock
- lateinit var transitions: Transitions
- @Mock
- lateinit var userRepositories: DesktopUserRepositories
- @Mock
- lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
- @Mock
- lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
+ @Mock lateinit var transitions: Transitions
+ @Mock lateinit var userRepositories: DesktopUserRepositories
+ @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
+ @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
@Mock
lateinit var desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler
- @Mock
- lateinit var desktopImmersiveController: DesktopImmersiveController
- @Mock
- lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock
- lateinit var mockHandler: Handler
- @Mock
- lateinit var closingTaskLeash: SurfaceControl
- @Mock
- lateinit var shellInit: ShellInit
- @Mock
- lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
- @Mock
- private lateinit var desktopRepository: DesktopRepository
+ @Mock lateinit var desktopImmersiveController: DesktopImmersiveController
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var mockHandler: Handler
+ @Mock lateinit var closingTaskLeash: SurfaceControl
+ @Mock lateinit var shellInit: ShellInit
+ @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var desktopRepository: DesktopRepository
private lateinit var mixedHandler: DesktopMixedTransitionHandler
-
@Before
fun setUp() {
whenever(userRepositories.current).thenReturn(desktopRepository)
@@ -157,11 +145,11 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@DisableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+ )
fun startRemoveTransition_callsFreeformTaskTransitionHandler() {
val wct = WindowContainerTransaction()
- whenever(freeformTaskTransitionHandler.startRemoveTransition(wct))
- .thenReturn(mock())
+ whenever(freeformTaskTransitionHandler.startRemoveTransition(wct)).thenReturn(mock())
mixedHandler.startRemoveTransition(wct)
@@ -171,7 +159,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+ )
fun startRemoveTransition_startsCloseTransition() {
val wct = WindowContainerTransaction()
whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -193,18 +182,19 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val transitionInfo =
createCloseTransitionInfo(
changeMode = TRANSIT_OPEN,
- task = createTask(WINDOWING_MODE_FREEFORM)
+ task = createTask(WINDOWING_MODE_FREEFORM),
)
whenever(freeformTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()))
.thenReturn(true)
- val started = mixedHandler.startAnimation(
- transition = transition,
- info = transitionInfo,
- startTransaction = mock(),
- finishTransaction = mock(),
- finishCallback = {}
- )
+ val started =
+ mixedHandler.startAnimation(
+ transition = transition,
+ info = transitionInfo,
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
assertFalse("Should not start animation without closing desktop task", started)
}
@@ -212,7 +202,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+ )
fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() {
val wct = WindowContainerTransaction()
val transition = mock<IBinder>()
@@ -225,13 +216,14 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
.thenReturn(transition)
mixedHandler.startRemoveTransition(wct)
- val started = mixedHandler.startAnimation(
- transition = transition,
- info = transitionInfo,
- startTransaction = mock(),
- finishTransaction = mock(),
- finishCallback = {}
- )
+ val started =
+ mixedHandler.startAnimation(
+ transition = transition,
+ info = transitionInfo,
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
assertTrue("Should delegate animation to close transition handler", started)
verify(closeDesktopTaskTransitionHandler)
@@ -241,12 +233,16 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+ )
fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() {
val wct = WindowContainerTransaction()
val transition = mock<IBinder>()
- val transitionInfo = createCloseTransitionInfo(
- task = createTask(WINDOWING_MODE_FREEFORM), withWallpaper = true)
+ val transitionInfo =
+ createCloseTransitionInfo(
+ task = createTask(WINDOWING_MODE_FREEFORM),
+ withWallpaper = true,
+ )
whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any()))
.thenReturn(mock())
whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -258,7 +254,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
info = transitionInfo,
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
verify(transitions)
@@ -268,14 +264,14 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
any(),
any(),
any(),
- eq(mixedHandler)
+ eq(mixedHandler),
)
verify(interactionJankMonitor)
.begin(
closingTaskLeash,
context,
mockHandler,
- CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
+ CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
)
}
@@ -283,7 +279,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@DisableFlags(
Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_doesNotUseMixedHandler() {
val wct = WindowContainerTransaction()
val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -294,7 +291,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
transitionType = TRANSIT_OPEN,
wct = wct,
taskId = task.taskId,
- exitingImmersiveTask = null
+ exitingImmersiveTask = null,
)
verify(transitions).startTransition(TRANSIT_OPEN, wct, /* handler= */ null)
@@ -312,7 +309,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
transitionType = TRANSIT_OPEN,
wct = wct,
taskId = task.taskId,
- exitingImmersiveTask = null
+ exitingImmersiveTask = null,
)
verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
@@ -321,7 +318,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun startLaunchTransition_desktopAppLaunchEnabled_usesMixedHandler() {
val wct = WindowContainerTransaction()
val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -332,7 +330,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
transitionType = TRANSIT_OPEN,
wct = wct,
taskId = task.taskId,
- exitingImmersiveTask = null
+ exitingImmersiveTask = null,
)
verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
@@ -357,24 +355,22 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val otherChange = createChange(createTask(WINDOWING_MODE_FREEFORM))
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange, otherChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, otherChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
-
- verify(transitions).dispatchTransition(
- eq(transition),
- argThat { info ->
- info.changes.contains(launchTaskChange) && info.changes.contains(otherChange)
- },
- any(),
- any(),
- any(),
- eq(mixedHandler),
- )
+ ) {}
+
+ verify(transitions)
+ .dispatchTransition(
+ eq(transition),
+ argThat { info ->
+ info.changes.contains(launchTaskChange) && info.changes.contains(otherChange)
+ },
+ any(),
+ any(),
+ any(),
+ eq(mixedHandler),
+ )
}
@Test
@@ -397,32 +393,32 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val immersiveChange = createChange(immersiveTask)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange, immersiveChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, immersiveChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
verify(desktopImmersiveController)
.animateResizeChange(eq(immersiveChange), any(), any(), any())
- verify(transitions).dispatchTransition(
- eq(transition),
- argThat { info ->
- info.changes.contains(launchTaskChange) && !info.changes.contains(immersiveChange)
- },
- any(),
- any(),
- any(),
- eq(mixedHandler),
- )
+ verify(transitions)
+ .dispatchTransition(
+ eq(transition),
+ argThat { info ->
+ info.changes.contains(launchTaskChange) &&
+ !info.changes.contains(immersiveChange)
+ },
+ any(),
+ any(),
+ any(),
+ eq(mixedHandler),
+ )
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -439,22 +435,19 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
- verify(rootTaskDisplayAreaOrganizer, times(0))
- .reparentToDisplayArea(anyInt(), any(), any())
+ verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -473,22 +466,20 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange, minimizeChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, minimizeChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
- verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
- anyInt(), eq(minimizeChange.leash), any())
+ verify(rootTaskDisplayAreaOrganizer)
+ .reparentToDisplayArea(anyInt(), eq(minimizeChange.leash), any())
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -505,15 +496,13 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
)
- val started = mixedHandler.startAnimation(
- transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(nonLaunchTaskChange)
- ),
- SurfaceControl.Transaction(),
- SurfaceControl.Transaction(),
- ) { }
+ val started =
+ mixedHandler.startAnimation(
+ transition,
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(nonLaunchTaskChange)),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) {}
assertFalse("Should not start animation without launching desktop task", started)
}
@@ -529,21 +518,18 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
.thenReturn(mock())
- mixedHandler.startLaunchTransition(
- transitionType = TRANSIT_OPEN,
- wct = wct,
- taskId = null,
- )
+ mixedHandler.startLaunchTransition(transitionType = TRANSIT_OPEN, wct = wct, taskId = null)
- val started = mixedHandler.startAnimation(
- transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(createChange(task, mode = TRANSIT_OPEN))
- ),
- StubTransaction(),
- StubTransaction(),
- ) { }
+ val started =
+ mixedHandler.startAnimation(
+ transition,
+ createCloseTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(createChange(task, mode = TRANSIT_OPEN)),
+ ),
+ StubTransaction(),
+ StubTransaction(),
+ ) {}
assertThat(started).isEqualTo(true)
}
@@ -569,15 +555,13 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val immersiveChange = createChange(immersiveTask, mode = TRANSIT_CHANGE)
val openingChange = createChange(openingTask, mode = TRANSIT_OPEN)
- val started = mixedHandler.startAnimation(
- transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(immersiveChange, openingChange)
- ),
- StubTransaction(),
- StubTransaction(),
- ) { }
+ val started =
+ mixedHandler.startAnimation(
+ transition,
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(immersiveChange, openingChange)),
+ StubTransaction(),
+ StubTransaction(),
+ ) {}
assertThat(started).isEqualTo(true)
verify(desktopImmersiveController)
@@ -587,7 +571,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -606,22 +591,19 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
- verify(rootTaskDisplayAreaOrganizer, times(0))
- .reparentToDisplayArea(anyInt(), any(), any())
+ verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
}
@Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+ )
fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -642,16 +624,13 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange, minimizeChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, minimizeChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
- verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
- anyInt(), eq(minimizeChange.leash), any())
+ verify(rootTaskDisplayAreaOrganizer)
+ .reparentToDisplayArea(anyInt(), eq(minimizeChange.leash), any())
}
@Test
@@ -672,13 +651,10 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val launchTaskChange = createChange(launchingTask)
mixedHandler.startAnimation(
transition,
- createCloseTransitionInfo(
- TRANSIT_OPEN,
- listOf(launchTaskChange)
- ),
+ createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
SurfaceControl.Transaction(),
SurfaceControl.Transaction(),
- ) { }
+ ) {}
assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
}
@@ -701,7 +677,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
mixedHandler.onTransitionConsumed(
transition = transition,
aborted = true,
- finishTransaction = SurfaceControl.Transaction()
+ finishTransaction = SurfaceControl.Transaction(),
)
assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
@@ -714,8 +690,14 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val transition = Binder()
whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
whenever(
- desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
- )
+ desktopBackNavigationTransitionHandler.startAnimation(
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ )
.thenReturn(true)
mixedHandler.addPendingMixedTransition(
PendingMixedTransition.Minimize(
@@ -726,24 +708,24 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
)
val minimizingTaskChange = createChange(minimizingTask)
- val started = mixedHandler.startAnimation(
- transition = transition,
- info =
- createCloseTransitionInfo(
- TRANSIT_TO_BACK,
- listOf(minimizingTaskChange)
- ),
- startTransaction = mock(),
- finishTransaction = mock(),
- finishCallback = {}
- )
+ val started =
+ mixedHandler.startAnimation(
+ transition = transition,
+ info = createCloseTransitionInfo(TRANSIT_TO_BACK, listOf(minimizingTaskChange)),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
assertTrue("Should delegate animation to back navigation transition handler", started)
verify(desktopBackNavigationTransitionHandler)
.startAnimation(
eq(transition),
argThat { info -> info.changes.contains(minimizingTaskChange) },
- any(), any(), any())
+ any(),
+ any(),
+ any(),
+ )
}
@Test
@@ -753,8 +735,14 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val transition = Binder()
whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
whenever(
- desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
- )
+ desktopBackNavigationTransitionHandler.startAnimation(
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ )
.thenReturn(true)
mixedHandler.addPendingMixedTransition(
PendingMixedTransition.Minimize(
@@ -767,14 +755,10 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
val minimizingTaskChange = createChange(minimizingTask)
mixedHandler.startAnimation(
transition = transition,
- info =
- createCloseTransitionInfo(
- TRANSIT_TO_BACK,
- listOf(minimizingTaskChange)
- ),
+ info = createCloseTransitionInfo(TRANSIT_TO_BACK, listOf(minimizingTaskChange)),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
verify(transitions)
@@ -784,7 +768,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
any(),
any(),
any(),
- eq(mixedHandler)
+ eq(mixedHandler),
)
}
@@ -814,14 +798,15 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
private fun createCloseTransitionInfo(
@TransitionType type: Int,
- changes: List<TransitionInfo.Change> = emptyList()
- ): TransitionInfo = TransitionInfo(type, /* flags= */ 0).apply {
- changes.forEach { change -> addChange(change) }
- }
+ changes: List<TransitionInfo.Change> = emptyList(),
+ ): TransitionInfo =
+ TransitionInfo(type, /* flags= */ 0).apply {
+ changes.forEach { change -> addChange(change) }
+ }
private fun createChange(
task: RunningTaskInfo,
- @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE
+ @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE,
): TransitionInfo.Change =
TransitionInfo.Change(task.token, SurfaceControl()).apply {
taskInfo = task
@@ -838,8 +823,6 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
RunningTaskInfo().apply {
token = WindowContainerToken(mock<IWindowContainerToken>())
baseIntent =
- Intent().apply {
- component = DesktopWallpaperActivity.wallpaperActivityComponent
- }
+ Intent().apply { component = DesktopWallpaperActivity.wallpaperActivityComponent }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 2f225f22cce0..abd707817621 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -53,9 +53,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/**
- * Tests for [DesktopModeEventLogger].
- */
+/** Tests for [DesktopModeEventLogger]. */
class DesktopModeEventLoggerTest : ShellTestCase() {
private val desktopModeEventLogger = DesktopModeEventLogger()
@@ -64,13 +62,13 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
@JvmField
@Rule(order = 0)
- val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .mockStatic(FrameworkStatsLog::class.java)
- .mockStatic(EventLogTags::class.java).build()!!
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(FrameworkStatsLog::class.java)
+ .mockStatic(EventLogTags::class.java)
+ .build()!!
- @JvmField
- @Rule(order = 1)
- val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule()
@Before
fun setUp() {
@@ -95,14 +93,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* exit_reason */
eq(0),
/* sessionId */
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellEnterDesktopMode(
eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -127,14 +125,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* exit_reason */
eq(0),
/* sessionId */
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellEnterDesktopMode(
eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -164,14 +162,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* exit_reason */
eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
/* sessionId */
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellExitDesktopMode(
eq(ExitReason.DRAG_TO_EXIT.reason),
- eq(sessionId)
+ eq(sessionId),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -214,16 +212,13 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(
- FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED
- ),
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
@@ -233,7 +228,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -275,16 +270,13 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(
- FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED
- ),
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
@@ -294,7 +286,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -339,7 +331,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -358,7 +350,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -399,7 +391,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* unminimize_reason */
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -418,7 +410,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
eq(MinimizeReason.TASK_LIMIT.reason),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -459,7 +451,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* unminimize_reason */
eq(UnminimizeReason.TASKBAR_TAP.reason),
/* visible_task_count */
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -478,7 +470,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UnminimizeReason.TASKBAR_TAP.reason),
- eq(TASK_COUNT)
+ eq(TASK_COUNT),
)
}
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -486,8 +478,11 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
@Test
fun logTaskResizingStarted_noOngoingSession_doesNotLog() {
- desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER,
- InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo())
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.CORNER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ createTaskInfo(),
+ )
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -498,19 +493,33 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() {
val sessionId = startDesktopModeSession()
- desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER,
- InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo(), TASK_SIZE_UPDATE.taskWidth,
- TASK_SIZE_UPDATE.taskHeight, displayController)
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.CORNER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ createTaskInfo(),
+ TASK_SIZE_UPDATE.taskWidth,
+ TASK_SIZE_UPDATE.taskHeight,
+ displayController,
+ )
verify {
FrameworkStatsLog.write(
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
/* resize_trigger */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER
+ ),
/* resizing_stage */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE
+ ),
/* input_method */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+ ),
/* desktop_mode_session_id */
eq(sessionId),
/* instance_id */
@@ -530,8 +539,11 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
@Test
fun logTaskResizingEnded_noOngoingSession_doesNotLog() {
- desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER,
- InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo())
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.CORNER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ createTaskInfo(),
+ )
verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -542,18 +554,31 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() {
val sessionId = startDesktopModeSession()
- desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER,
- InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo(), displayController = displayController)
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.CORNER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ createTaskInfo(),
+ displayController = displayController,
+ )
verify {
FrameworkStatsLog.write(
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
/* resize_trigger */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER
+ ),
/* resizing_stage */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE
+ ),
/* input_method */
- eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+ ),
/* desktop_mode_session_id */
eq(sessionId),
/* instance_id */
@@ -582,9 +607,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
fun logTaskInfoStateInit_logsTaskInfoChangedStateInit() {
desktopModeEventLogger.logTaskInfoStateInit()
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
- eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD),
+ eq(
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD
+ ),
/* instance_id */
eq(0),
/* uid */
@@ -604,13 +632,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* unminimize_reason */
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(0)
+ eq(0),
)
}
}
private fun createTaskInfo(): RunningTaskInfo {
- return TestRunningTaskInfoBuilder().setTaskId(TASK_ID)
+ return TestRunningTaskInfoBuilder()
+ .setTaskId(TASK_ID)
.setUid(TASK_UID)
.setBounds(Rect(TASK_X, TASK_Y, TASK_WIDTH, TASK_HEIGHT))
.build()
@@ -628,27 +657,42 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
private const val DISPLAY_HEIGHT = 500
private const val DISPLAY_AREA = DISPLAY_HEIGHT * DISPLAY_WIDTH
- private val TASK_UPDATE = TaskUpdate(
- TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y,
- visibleTaskCount = TASK_COUNT,
- )
+ private val TASK_UPDATE =
+ TaskUpdate(
+ TASK_ID,
+ TASK_UID,
+ TASK_HEIGHT,
+ TASK_WIDTH,
+ TASK_X,
+ TASK_Y,
+ visibleTaskCount = TASK_COUNT,
+ )
- private val TASK_SIZE_UPDATE = TaskSizeUpdate(
- resizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
- inputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
- TASK_ID,
- TASK_UID,
- TASK_HEIGHT,
- TASK_WIDTH,
- DISPLAY_AREA,
- )
+ private val TASK_SIZE_UPDATE =
+ TaskSizeUpdate(
+ resizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
+ inputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
+ TASK_ID,
+ TASK_UID,
+ TASK_HEIGHT,
+ TASK_WIDTH,
+ DISPLAY_AREA,
+ )
private fun createTaskUpdate(
minimizeReason: MinimizeReason? = null,
unminimizeReason: UnminimizeReason? = null,
- ) = TaskUpdate(
- TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
- unminimizeReason, TASK_COUNT
- )
+ ) =
+ TaskUpdate(
+ TASK_ID,
+ TASK_UID,
+ TASK_HEIGHT,
+ TASK_WIDTH,
+ TASK_X,
+ TASK_Y,
+ minimizeReason,
+ unminimizeReason,
+ TASK_COUNT,
+ )
}
}
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 e57ae2a86859..413e7bc5d1d6 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
@@ -30,49 +30,49 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.KeyEvent
import android.window.DisplayAreaInfo
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
+import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
-import com.android.wm.shell.transition.FocusTransitionObserver
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.whenever
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
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
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.setMain
import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
/**
@@ -130,21 +130,24 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
doAnswer {
- keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
- null
- }.whenever(inputManager).registerKeyGestureEventHandler(any())
+ keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
+ null
+ }
+ .whenever(inputManager)
+ .registerKeyGestureEventHandler(any())
shellInit.init()
- desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
- context,
- Optional.of(desktopModeWindowDecorViewModel),
- Optional.of(desktopTasksController),
- inputManager,
- shellTaskOrganizer,
- focusTransitionObserver,
- testExecutor,
- displayController
- )
+ desktopModeKeyGestureHandler =
+ DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel),
+ Optional.of(desktopTasksController),
+ inputManager,
+ shellTaskOrganizer,
+ focusTransitionObserver,
+ testExecutor,
+ displayController,
+ )
}
@After
@@ -160,7 +163,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
@EnableFlags(
FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
)
fun keyGestureMoveToNextDisplay_shouldMoveToNextDisplay() {
// Set up two display ids
@@ -176,12 +179,13 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
- .setDisplayId(SECOND_DISPLAY)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
- .build()
+ val event =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
+ .setDisplayId(SECOND_DISPLAY)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
+ .build()
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
@@ -190,108 +194,102 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
- FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
- )
+ @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
fun keyGestureSnapLeft_shouldSnapResizeTaskToLeft() {
val task = setUpFreeformTask()
task.isFocused = true
whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
- .setModifierState(KeyEvent.META_META_ON)
- .build()
+ val event =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
assertThat(result).isTrue()
- verify(desktopModeWindowDecorViewModel).onSnapResize(
- task.taskId,
- true,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ verify(desktopModeWindowDecorViewModel)
+ .onSnapResize(
+ task.taskId,
+ true,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false,
+ )
}
@Test
- @EnableFlags(
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
- FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
- )
+ @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
fun keyGestureSnapRight_shouldSnapResizeTaskToRight() {
val task = setUpFreeformTask()
task.isFocused = true
whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
- .setModifierState(KeyEvent.META_META_ON)
- .build()
+ val event =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
assertThat(result).isTrue()
- verify(desktopModeWindowDecorViewModel).onSnapResize(
- task.taskId,
- false,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ verify(desktopModeWindowDecorViewModel)
+ .onSnapResize(
+ task.taskId,
+ false,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false,
+ )
}
@Test
- @EnableFlags(
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
- FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
- )
+ @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
fun keyGestureToggleFreeformWindowSize_shouldToggleTaskSize() {
val task = setUpFreeformTask()
task.isFocused = true
whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
- .setModifierState(KeyEvent.META_META_ON)
- .build()
+ val event =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
assertThat(result).isTrue()
- verify(desktopTasksController).toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- isMaximized = isTaskMaximized(task, displayController),
- source = ToggleTaskSizeInteraction.Source.KEYBOARD_SHORTCUT,
- inputMethod =
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- ),
- )
+ verify(desktopTasksController)
+ .toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ isMaximized = isTaskMaximized(task, displayController),
+ source = ToggleTaskSizeInteraction.Source.KEYBOARD_SHORTCUT,
+ inputMethod = DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ ),
+ )
}
@Test
- @EnableFlags(
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
- FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
- )
+ @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
fun keyGestureMinimizeFreeformWindow_shouldMinimizeTask() {
val task = setUpFreeformTask()
task.isFocused = true
whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
- .setModifierState(KeyEvent.META_META_ON)
- .build()
+ val event =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 7c4ce4acfc9c..43684fb92b64 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -87,700 +87,735 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
- @JvmField
- @Rule
- val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeStatus::class.java)
- .mockStatic(SystemProperties::class.java)
- .mockStatic(Trace::class.java)
- .build()!!
-
- private val testExecutor = mock<ShellExecutor>()
- private val mockShellInit = mock<ShellInit>()
- private val transitions = mock<Transitions>()
- private val context = mock<Context>()
-
- private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
- private lateinit var shellInit: ShellInit
- private lateinit var desktopModeEventLogger: DesktopModeEventLogger
-
- @Before
- fun setup() {
- whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
- shellInit = spy(ShellInit(testExecutor))
- desktopModeEventLogger = mock<DesktopModeEventLogger>()
-
- transitionObserver = DesktopModeLoggerTransitionObserver(
- context, mockShellInit, transitions, desktopModeEventLogger)
- val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
- initRunnableCaptor.value.run()
- // verify this initialisation interaction to leave the desktopmodeEventLogger mock in a
- // consistent state with no outstanding interactions when test cases start executing.
- verify(desktopModeEventLogger).logTaskInfoStateInit()
- }
-
- @Test
- fun testInitialiseVisibleTasksSystemProperty() {
- ExtendedMockito.verify {
- SystemProperties.set(
- eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
- eq(DesktopModeLoggerTransitionObserver
- .VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE))
- }
- }
-
- @Test
- fun testRegistersObserverAtInit() {
- verify(transitions).registerObserver(same(transitionObserver))
- }
-
- @Test
- fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, never()).logSessionEnter(any())
- verify(desktopModeEventLogger, never()).logTaskAdded(any())
- }
-
- @Test
- fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- // task change is finalised when drag ends
- val transitionInfo =
- TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitToFront_logTaskAddedAndEnterReasonOverview() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
- // previous exit to overview transition
- // add a freeform task
- val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.isSessionActive = true
- val previousTransitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
- .build()
-
- callOnTransitionReady(previousTransitionInfo)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
-
- // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
- // next transition involving freeform windows
-
- // TRANSIT_TO_FRONT
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
- // previous exit to overview transition
- // add a freeform task
- val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.isSessionActive = true
- val previousTransitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
- .build()
-
- callOnTransitionReady(previousTransitionInfo)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
-
- // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
- // next transition involving freeform windows
-
- // TRANSIT_CHANGE
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
- // previous exit to overview transition
- // add a freeform task
- val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.isSessionActive = true
- val previousTransitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
- .build()
-
- callOnTransitionReady(previousTransitionInfo)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
-
- // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
- // next transition involving freeform windows
-
- // TRANSIT_OPEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- @Suppress("ktlint:standard:max-line-length")
- fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
- // Tests for AppFromOverview precedence in compared to cancelled Overview
-
- // previous exit to overview transition
- // add a freeform task
- val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.isSessionActive = true
- val previousTransitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
- .build()
-
- callOnTransitionReady(previousTransitionInfo)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
-
- // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
- val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
- // Previous Exit reason recorded as Screen Off
- transitionObserver.addTaskInfosToCachedMap(freeformTask)
- transitionObserver.isSessionActive = true
- callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
- verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
- // Enter desktop through back transition, this happens when user enters after dismissing
- // keyguard
- val change = createChange(TRANSIT_TO_FRONT, freeformTask)
- val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
- val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
- // Previous Exit reason recorded as Screen Off
- transitionObserver.addTaskInfosToCachedMap(freeformTask)
- transitionObserver.isSessionActive = true
- callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
- verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
-
- // Enter desktop through app handle drag. This represents cases where instead of moving to
- // desktop right after turning the screen on, we move to fullscreen then move another task
- // to desktop
- val transitionInfo =
- TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
- .addChange(createChange(TRANSIT_TO_FRONT, freeformTask))
- .build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
- .addChange(change)
- .build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // recents transition
- val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
- }
-
- @Test
- fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // task closing
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
- }
-
- @Test
- fun transitMinimize_logExitReasongMinimized() {
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // minimize the task
- val change = createChange(TRANSIT_MINIMIZE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_MINIMIZE).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- assertFalse(transitionObserver.isSessionActive)
- verify(desktopModeEventLogger, times(1)).logSessionExit(eq(ExitReason.TASK_MINIMIZED))
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(DEFAULT_TASK_UPDATE))
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
- // add a freeform task to an existing session
- val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.isSessionActive = true
-
- // recents transition sent freeform window to back
- val change = createChange(TRANSIT_TO_BACK, taskInfo)
- val transitionInfo1 =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
- callOnTransitionReady(transitionInfo1)
-
- verifyTaskRemovedAndExitLogging(
- ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
- )
-
- val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
- callOnTransitionReady(transitionInfo2)
-
- verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
- DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
- }
-
- @Test
- fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
- // add an existing freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.isSessionActive = true
-
- // new freeform task added
- val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
- verify(desktopModeEventLogger, never()).logSessionEnter(any())
- }
-
- @Test
- fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
- // add an existing freeform task
- val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.isSessionActive = true
-
- // task position changed
- val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_CHANGE, 0)
- .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
- .build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskInfoChanged(
- eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(DesktopModeStatus::class.java)
+ .mockStatic(SystemProperties::class.java)
+ .mockStatic(Trace::class.java)
+ .build()!!
+
+ private val testExecutor = mock<ShellExecutor>()
+ private val mockShellInit = mock<ShellInit>()
+ private val transitions = mock<Transitions>()
+ private val context = mock<Context>()
+
+ private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+
+ @Before
+ fun setup() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+ shellInit = spy(ShellInit(testExecutor))
+ desktopModeEventLogger = mock<DesktopModeEventLogger>()
+
+ transitionObserver =
+ DesktopModeLoggerTransitionObserver(
+ context,
+ mockShellInit,
+ transitions,
+ desktopModeEventLogger,
+ )
+ val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mockShellInit)
+ .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+ initRunnableCaptor.value.run()
+ // verify this initialisation interaction to leave the desktopmodeEventLogger mock in a
+ // consistent state with no outstanding interactions when test cases start executing.
+ verify(desktopModeEventLogger).logTaskInfoStateInit()
+ }
+
+ @Test
+ fun testInitialiseVisibleTasksSystemProperty() {
+ ExtendedMockito.verify {
+ SystemProperties.set(
+ eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+ eq(
+ DesktopModeLoggerTransitionObserver
+ .VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE
+ ),
+ )
+ }
+ }
+
+ @Test
+ fun testRegistersObserverAtInit() {
+ verify(transitions).registerObserver(same(transitionObserver))
+ }
+
+ @Test
+ fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, never()).logSessionEnter(any())
+ verify(desktopModeEventLogger, never()).logTaskAdded(any())
+ }
+
+ @Test
+ fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_FREEFORM_INTENT,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
)
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
- // add an existing freeform task
- val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
- transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.isSessionActive = true
-
- // task resized
- val newTaskInfo =
- createTaskInfo(
- WINDOWING_MODE_FREEFORM,
- taskWidth = DEFAULT_TASK_WIDTH + 100,
- taskHeight = DEFAULT_TASK_HEIGHT - 100)
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_CHANGE, 0)
- .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
- .build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskInfoChanged(
- eq(
- DEFAULT_TASK_UPDATE.copy(
- taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
- visibleTaskCount = 1))
+ }
+
+ @Test
+ fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ // task change is finalised when drag ends
+ val transitionInfo =
+ TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_HANDLE_DRAG,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
)
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
- // add 2 existing freeform task
- val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
- val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
- transitionObserver.addTaskInfosToCachedMap(taskInfo1)
- transitionObserver.addTaskInfosToCachedMap(taskInfo2)
- transitionObserver.isSessionActive = true
-
- // task 1 position update
- val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
- val transitionInfo1 =
- TransitionInfoBuilder(TRANSIT_CHANGE, 0)
- .addChange(createChange(TRANSIT_CHANGE, newTaskInfo1))
- .build()
- callOnTransitionReady(transitionInfo1)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskInfoChanged(
- eq(DEFAULT_TASK_UPDATE.copy(
- taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+ }
+
+ @Test
+ fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_HANDLE_MENU_BUTTON,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
)
- verifyZeroInteractions(desktopModeEventLogger)
-
- // task 2 resize
- val newTaskInfo2 =
- createTaskInfo(
- WINDOWING_MODE_FREEFORM,
- id = 2,
- taskWidth = DEFAULT_TASK_WIDTH + 100,
- taskHeight = DEFAULT_TASK_HEIGHT - 100)
- val transitionInfo2 =
- TransitionInfoBuilder(TRANSIT_CHANGE, 0)
- .addChange(createChange(TRANSIT_CHANGE, newTaskInfo2))
- .build()
-
- callOnTransitionReady(transitionInfo2)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskInfoChanged(
- eq(
- DEFAULT_TASK_UPDATE.copy(
- instanceId = 2,
- taskWidth = DEFAULT_TASK_WIDTH + 100,
- taskHeight = DEFAULT_TASK_HEIGHT - 100,
- visibleTaskCount = 2)),
- )
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
- // add two existing freeform tasks
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
- transitionObserver.isSessionActive = true
-
- // new freeform task closed
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1))
- .logTaskRemoved(
- eq(DEFAULT_TASK_UPDATE.copy(
- instanceId = 2, visibleTaskCount = 1))
+ }
+
+ @Test
+ fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_FROM_OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
)
- verify(desktopModeEventLogger, never()).logSessionExit(any())
- }
-
- /** Simulate calling the onTransitionReady() method */
- private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
- val transition = mock<IBinder>()
- val startT = mock<SurfaceControl.Transaction>()
- val finishT = mock<SurfaceControl.Transaction>()
-
- transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
- }
-
- private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
- assertTrue(transitionObserver.isSessionActive)
- verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
- ExtendedMockito.verify {
- Trace.setCounter(
- eq(Trace.TRACE_TAG_WINDOW_MANAGER),
- eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
- eq(taskUpdate.visibleTaskCount.toLong()))
- }
- ExtendedMockito.verify {
- SystemProperties.set(
- eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
- eq(taskUpdate.visibleTaskCount.toString()))
- }
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- private fun verifyTaskRemovedAndExitLogging(
- exitReason: ExitReason,
- taskUpdate: TaskUpdate
- ) {
- assertFalse(transitionObserver.isSessionActive)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
- verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- private companion object {
- const val DEFAULT_TASK_ID = 1
- const val DEFAULT_TASK_UID = 2
- const val DEFAULT_TASK_HEIGHT = 100
- const val DEFAULT_TASK_WIDTH = 200
- const val DEFAULT_TASK_X = 30
- const val DEFAULT_TASK_Y = 70
- const val DEFAULT_VISIBLE_TASK_COUNT = 0
- val DEFAULT_TASK_UPDATE =
- TaskUpdate(
- DEFAULT_TASK_ID,
- DEFAULT_TASK_UID,
- DEFAULT_TASK_HEIGHT,
- DEFAULT_TASK_WIDTH,
- DEFAULT_TASK_X,
- DEFAULT_TASK_Y,
- visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT,
+ }
+
+ @Test
+ fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.KEYBOARD_SHORTCUT_ENTER,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
)
+ }
+
+ @Test
+ fun transitToFront_logTaskAddedAndEnterReasonOverview() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
- fun createTaskInfo(
- windowMode: Int,
- id: Int = DEFAULT_TASK_ID,
- uid: Int = DEFAULT_TASK_UID,
- taskHeight: Int = DEFAULT_TASK_HEIGHT,
- taskWidth: Int = DEFAULT_TASK_WIDTH,
- taskX: Int = DEFAULT_TASK_X,
- taskY: Int = DEFAULT_TASK_Y,
- ) =
- ActivityManager.RunningTaskInfo().apply {
- taskId = id
- effectiveUid = uid
- configuration.windowConfiguration.apply {
- windowingMode = windowMode
- positionInParent = Point(taskX, taskY)
- bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
- }
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ // add a freeform task
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+ transitionObserver.isSessionActive = true
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_TO_FRONT
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ // add a freeform task
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+ transitionObserver.isSessionActive = true
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_CHANGE
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ // add a freeform task
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+ transitionObserver.isSessionActive = true
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_OPEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ @Suppress("ktlint:standard:max-line-length")
+ fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+ // Tests for AppFromOverview precedence in compared to cancelled Overview
+
+ // previous exit to overview transition
+ // add a freeform task
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+ transitionObserver.isSessionActive = true
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+ // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_FROM_OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.UNKNOWN_ENTER,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.SCREEN_ON,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
+ val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ // Previous Exit reason recorded as Screen Off
+ transitionObserver.addTaskInfosToCachedMap(freeformTask)
+ transitionObserver.isSessionActive = true
+ callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+ // Enter desktop through back transition, this happens when user enters after dismissing
+ // keyguard
+ val change = createChange(TRANSIT_TO_FRONT, freeformTask)
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.SCREEN_ON,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
+ val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ // Previous Exit reason recorded as Screen Off
+ transitionObserver.addTaskInfosToCachedMap(freeformTask)
+ transitionObserver.isSessionActive = true
+ callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+
+ // Enter desktop through app handle drag. This represents cases where instead of moving to
+ // desktop right after turning the screen on, we move to fullscreen then move another task
+ // to desktop
+ val transitionInfo =
+ TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+ .addChange(createChange(TRANSIT_TO_FRONT, freeformTask))
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.APP_HANDLE_DRAG,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
+ .addChange(change)
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT)
+ .addChange(change)
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // recents transition
+ val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(change)
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // task closing
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
+ fun transitMinimize_logExitReasongMinimized() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // minimize the task
+ val change = createChange(TRANSIT_MINIMIZE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_MINIMIZE).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ assertFalse(transitionObserver.isSessionActive)
+ verify(desktopModeEventLogger, times(1)).logSessionExit(eq(ExitReason.TASK_MINIMIZED))
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(DEFAULT_TASK_UPDATE))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
+ // add a freeform task to an existing session
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
+ transitionObserver.isSessionActive = true
+
+ // recents transition sent freeform window to back
+ val change = createChange(TRANSIT_TO_BACK, taskInfo)
+ val transitionInfo1 =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(change)
+ .build()
+ callOnTransitionReady(transitionInfo1)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+ val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
+ callOnTransitionReady(transitionInfo2)
+
+ verifyTaskAddedAndEnterLogging(
+ EnterReason.OVERVIEW,
+ DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+ )
+ }
+
+ @Test
+ fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
+ // add an existing freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // new freeform task added
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
+ verify(desktopModeEventLogger, never()).logSessionEnter(any())
+ }
+
+ @Test
+ fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
+ // add an existing freeform task
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
+ transitionObserver.isSessionActive = true
+
+ // task position changed
+ val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+ )
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
+ // add an existing freeform task
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
+ transitionObserver.isSessionActive = true
+
+ // task resized
+ val newTaskInfo =
+ createTaskInfo(
+ WINDOWING_MODE_FREEFORM,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100,
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100,
+ visibleTaskCount = 1,
+ )
+ )
+ )
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
+ // add 2 existing freeform task
+ val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo1)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo2)
+ transitionObserver.isSessionActive = true
+
+ // task 1 position update
+ val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+ val transitionInfo1 =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo1))
+ .build()
+ callOnTransitionReady(transitionInfo1)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+ )
+ verifyZeroInteractions(desktopModeEventLogger)
+
+ // task 2 resize
+ val newTaskInfo2 =
+ createTaskInfo(
+ WINDOWING_MODE_FREEFORM,
+ id = 2,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100,
+ )
+ val transitionInfo2 =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo2))
+ .build()
+
+ callOnTransitionReady(transitionInfo2)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100,
+ visibleTaskCount = 2,
+ )
+ )
+ )
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
+ // add two existing freeform tasks
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+ transitionObserver.isSessionActive = true
+
+ // new freeform task closed
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 1)))
+ verify(desktopModeEventLogger, never()).logSessionExit(any())
+ }
+
+ /** Simulate calling the onTransitionReady() method */
+ private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+ val transition = mock<IBinder>()
+ val startT = mock<SurfaceControl.Transaction>()
+ val finishT = mock<SurfaceControl.Transaction>()
+
+ transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
+ }
+
+ private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
+ assertTrue(transitionObserver.isSessionActive)
+ verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
+ ExtendedMockito.verify {
+ Trace.setCounter(
+ eq(Trace.TRACE_TAG_WINDOW_MANAGER),
+ eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
+ eq(taskUpdate.visibleTaskCount.toLong()),
+ )
}
+ ExtendedMockito.verify {
+ SystemProperties.set(
+ eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+ eq(taskUpdate.visibleTaskCount.toString()),
+ )
+ }
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
+ assertFalse(transitionObserver.isSessionActive)
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
+ verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ private companion object {
+ const val DEFAULT_TASK_ID = 1
+ const val DEFAULT_TASK_UID = 2
+ const val DEFAULT_TASK_HEIGHT = 100
+ const val DEFAULT_TASK_WIDTH = 200
+ const val DEFAULT_TASK_X = 30
+ const val DEFAULT_TASK_Y = 70
+ const val DEFAULT_VISIBLE_TASK_COUNT = 0
+ val DEFAULT_TASK_UPDATE =
+ TaskUpdate(
+ DEFAULT_TASK_ID,
+ DEFAULT_TASK_UID,
+ DEFAULT_TASK_HEIGHT,
+ DEFAULT_TASK_WIDTH,
+ DEFAULT_TASK_X,
+ DEFAULT_TASK_Y,
+ visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT,
+ )
- fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
- val change =
- Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
- change.mode = mode
- change.taskInfo = taskInfo
- return change
+ fun createTaskInfo(
+ windowMode: Int,
+ id: Int = DEFAULT_TASK_ID,
+ uid: Int = DEFAULT_TASK_UID,
+ taskHeight: Int = DEFAULT_TASK_HEIGHT,
+ taskWidth: Int = DEFAULT_TASK_WIDTH,
+ taskX: Int = DEFAULT_TASK_X,
+ taskY: Int = DEFAULT_TASK_Y,
+ ) =
+ ActivityManager.RunningTaskInfo().apply {
+ taskId = id
+ effectiveUid = uid
+ configuration.windowConfiguration.apply {
+ windowingMode = windowMode
+ positionInParent = Point(taskX, taskY)
+ bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
+ }
+ }
+
+ fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
+ val change =
+ Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
+ change.mode = mode
+ change.taskInfo = taskInfo
+ return change
+ }
}
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
index db4e93de9541..f6eed5da6cad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
@@ -18,18 +18,18 @@ package com.android.wm.shell.desktopmode
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType
-import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.TASK_DRAG
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
@@ -53,8 +53,7 @@ class DesktopModeTransitionTypesTest {
.isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON)
assertThat(APP_FROM_OVERVIEW.getEnterTransitionType())
.isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW)
- assertThat(TASK_DRAG.getEnterTransitionType())
- .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN)
+ assertThat(TASK_DRAG.getEnterTransitionType()).isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN)
assertThat(KEYBOARD_SHORTCUT.getEnterTransitionType())
.isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
index 94698e2fc0fb..72b1fd9af117 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.desktopmode
-
import android.content.ComponentName
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
@@ -67,8 +66,7 @@ class DesktopModeUiEventLoggerTest : ShellTestCase() {
@Test
fun log_eventLogged() {
- val event =
- DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
logger.log(UID, PACKAGE_NAME, event)
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
@@ -97,8 +95,7 @@ class DesktopModeUiEventLoggerTest : ShellTestCase() {
@Test
fun logWithInstanceId_eventLogged() {
- val event =
- DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event)
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
@@ -109,12 +106,12 @@ class DesktopModeUiEventLoggerTest : ShellTestCase() {
@Test
fun logWithTaskInfo_eventLogged() {
- val event =
- DESKTOP_WINDOW_EDGE_DRAG_RESIZE
- val taskInfo = TestRunningTaskInfoBuilder()
- .setUserId(USER_ID)
- .setBaseActivity(ComponentName(PACKAGE_NAME, "test"))
- .build()
+ val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ val taskInfo =
+ TestRunningTaskInfoBuilder()
+ .setUserId(USER_ID)
+ .setBaseActivity(ComponentName(PACKAGE_NAME, "test"))
+ .build()
whenever(mockPackageManager.getApplicationInfoAsUser(PACKAGE_NAME, /* flags= */ 0, USER_ID))
.thenReturn(ApplicationInfo().apply { uid = UID })
logger.log(taskInfo, event)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 935e6d052f5e..e46d2c7147ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -66,74 +66,109 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
fun testFullscreenRegionCalculation() {
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
- assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
- 2 * STABLE_INSETS.top))
+ assertThat(testRegion.bounds)
+ .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * STABLE_INSETS.top))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
val transitionHeight = SystemBarUtils.getStatusBarHeight(context)
- val toFullscreenScale = mContext.resources.getFloat(
- R.dimen.desktop_mode_fullscreen_region_scale
- )
+ val toFullscreenScale =
+ mContext.resources.getFloat(R.dimen.desktop_mode_fullscreen_region_scale)
val toFullscreenWidth = displayLayout.width() * toFullscreenScale
- assertThat(testRegion.bounds).isEqualTo(Rect(
- (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
- Short.MIN_VALUE.toInt(),
- (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
- transitionHeight))
+ assertThat(testRegion.bounds)
+ .isEqualTo(
+ Rect(
+ (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
+ Short.MIN_VALUE.toInt(),
+ (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
+ transitionHeight,
+ )
+ )
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
- assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
- 2 * STABLE_INSETS.top))
+ assertThat(testRegion.bounds)
+ .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * STABLE_INSETS.top))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
- assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
- transitionHeight))
+ assertThat(testRegion.bounds)
+ .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, transitionHeight))
}
@Test
fun testSplitLeftRegionCalculation() {
- val transitionHeight = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_split_from_desktop_height)
+ val transitionHeight =
+ context.resources.getDimensionPixelSize(R.dimen.desktop_mode_split_from_desktop_height)
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
- var testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ var testRegion =
+ visualIndicator.calculateSplitLeftRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
- testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitLeftRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
- testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitLeftRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
- testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitLeftRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
}
@Test
fun testSplitRightRegionCalculation() {
- val transitionHeight = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_split_from_desktop_height)
+ val transitionHeight =
+ context.resources.getDimensionPixelSize(R.dimen.desktop_mode_split_from_desktop_height)
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
- var testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ var testRegion =
+ visualIndicator.calculateSplitRightRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
- testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitRightRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
- testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitRightRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
- testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
- TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ testRegion =
+ visualIndicator.calculateSplitRightRegion(
+ displayLayout,
+ TRANSITION_AREA_WIDTH,
+ CAPTION_HEIGHT,
+ )
assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
}
@@ -141,10 +176,12 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
fun testDefaultIndicators() {
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
var result = visualIndicator.updateIndicatorType(PointF(-10000f, 500f))
- assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
result = visualIndicator.updateIndicatorType(PointF(10000f, 500f))
- assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
result = visualIndicator.updateIndicatorType(PointF(500f, 10000f))
assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
@@ -154,8 +191,16 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
}
private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
- visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
- context, taskSurface, taskDisplayAreaOrganizer, dragStartState)
+ visualIndicator =
+ DesktopModeVisualIndicator(
+ syncQueue,
+ taskInfo,
+ displayController,
+ context,
+ taskSurface,
+ taskDisplayAreaOrganizer,
+ dragStartState,
+ )
}
companion object {
@@ -163,11 +208,12 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
private const val CAPTION_HEIGHT = 50
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private const val NAVBAR_HEIGHT = 50
- private val STABLE_INSETS = Rect(
- DISPLAY_BOUNDS.left,
- DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
- DISPLAY_BOUNDS.right,
- DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
- )
+ private val STABLE_INSETS =
+ Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+ )
}
-} \ No newline at end of file
+}
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 344140d91ab3..e777ec7b55f6 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
@@ -76,15 +76,9 @@ class DesktopRepositoryTest : ShellTestCase() {
datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
shellInit = spy(ShellInit(testExecutor))
- repo =
- DesktopRepository(
- persistentRepository,
- datastoreScope,
- DEFAULT_USER_ID
- )
- whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
- Desktop.getDefaultInstance()
- )
+ repo = DesktopRepository(persistentRepository, datastoreScope, DEFAULT_USER_ID)
+ whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+ .thenReturn(Desktop.getDefaultInstance())
shellInit.init()
}
@@ -245,7 +239,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(1)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf()
+ freeformTasksInZOrder = arrayListOf(),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -253,7 +247,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(1, 2)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf()
+ freeformTasksInZOrder = arrayListOf(),
)
}
}
@@ -441,8 +435,8 @@ class DesktopRepositoryTest : ShellTestCase() {
}
/**
- * When a task vanishes, the displayId of the task is set to INVALID_DISPLAY.
- * This tests that task is removed from the last parent display when it vanishes.
+ * When a task vanishes, the displayId of the task is set to INVALID_DISPLAY. This tests that
+ * task is removed from the last parent display when it vanishes.
*/
@Test
fun updateTask_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
@@ -562,7 +556,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(5)
+ freeformTasksInZOrder = arrayListOf(5),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -570,7 +564,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(5)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(6, 5)
+ freeformTasksInZOrder = arrayListOf(6, 5),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -578,10 +572,10 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(5, 6)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(7, 6, 5)
+ freeformTasksInZOrder = arrayListOf(7, 6, 5),
)
}
- }
+ }
@Test
fun addTask_alreadyExists_movesToTop() {
@@ -628,7 +622,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(5)
+ freeformTasksInZOrder = arrayListOf(5),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -636,7 +630,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(5)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(6, 5)
+ freeformTasksInZOrder = arrayListOf(6, 5),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -644,7 +638,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(5, 6)),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(7, 6, 5)
+ freeformTasksInZOrder = arrayListOf(7, 6, 5),
)
verify(persistentRepository, times(2))
.addOrUpdateDesktop(
@@ -652,10 +646,10 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(arrayOf(5, 7)),
minimizedTasks = ArraySet(arrayOf(6)),
- freeformTasksInZOrder = arrayListOf(7, 6, 5)
+ freeformTasksInZOrder = arrayListOf(7, 6, 5),
)
}
- }
+ }
@Test
fun addTask_taskIsUnminimized_noop() {
@@ -694,7 +688,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(1)
+ freeformTasksInZOrder = arrayListOf(1),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -702,7 +696,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = ArrayList()
+ freeformTasksInZOrder = ArrayList(),
)
}
}
@@ -731,7 +725,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(1)
+ freeformTasksInZOrder = arrayListOf(1),
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -739,7 +733,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = ArrayList()
+ freeformTasksInZOrder = ArrayList(),
)
}
}
@@ -768,7 +762,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = arrayListOf(1)
+ freeformTasksInZOrder = arrayListOf(1),
)
verify(persistentRepository, never())
.addOrUpdateDesktop(
@@ -776,7 +770,7 @@ class DesktopRepositoryTest : ShellTestCase() {
DEFAULT_DESKTOP_ID,
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
- freeformTasksInZOrder = ArrayList()
+ freeformTasksInZOrder = ArrayList(),
)
}
}
@@ -928,7 +922,6 @@ class DesktopRepositoryTest : ShellTestCase() {
assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
}
-
@Test
fun updateTask_minimizedTaskBecomesVisible_unminimizesTask() {
repo.minimizeTask(displayId = 10, taskId = 2)
@@ -1056,6 +1049,7 @@ class DesktopRepositoryTest : ShellTestCase() {
class TestListener : DesktopRepository.ActiveTasksListener {
var activeChangesOnDefaultDisplay = 0
var activeChangesOnSecondaryDisplay = 0
+
override fun onActiveTasksChanged(displayId: Int) {
when (displayId) {
DEFAULT_DISPLAY -> activeChangesOnDefaultDisplay++
@@ -1093,4 +1087,4 @@ class DesktopRepositoryTest : ShellTestCase() {
private const val DEFAULT_USER_ID = 1000
private const val DEFAULT_DESKTOP_ID = 0
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index b4daa6637f83..19ab9113bc7a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -22,7 +22,6 @@ import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
-import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
@@ -45,167 +44,144 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopTaskChangeListenerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
- private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
+ private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
- private val desktopUserRepositories = mock<DesktopUserRepositories>()
- private val desktopRepository = mock<DesktopRepository>()
+ private val desktopUserRepositories = mock<DesktopUserRepositories>()
+ private val desktopRepository = mock<DesktopRepository>()
- @Before
- fun setUp() {
- desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
+ @Before
+ fun setUp() {
+ desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
- whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
- whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
- }
+ whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
+ whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
+ }
- @Test
- fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
- val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(false)
+ @Test
+ fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
- desktopTaskChangeListener.onTaskOpening(task)
+ desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopUserRepositories.current, never())
- .addTask(task.displayId, task.taskId, task.isVisible)
- verify(desktopUserRepositories.current, never())
- .removeFreeformTask(task.displayId, task.taskId)
- }
+ verify(desktopUserRepositories.current, never())
+ .addTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current, never())
+ .removeFreeformTask(task.displayId, task.taskId)
+ }
- @Test
- fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
- val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskOpening(task)
-
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
- }
+ @Test
+ fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
- @Test
- fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
- val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(false)
-
- desktopTaskChangeListener.onTaskOpening(task)
-
- verify(desktopUserRepositories.current)
- .addTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
- val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskOpening(task)
-
- verify(desktopUserRepositories.current)
- .addTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
- val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskChanging(task)
-
- verify(desktopUserRepositories.current)
- .removeFreeformTask(task.displayId, task.taskId)
- }
-
- @Test
- fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
- val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskChanging(task)
-
- verify(desktopUserRepositories.current)
- .updateTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
- val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskChanging(task)
-
- verify(desktopUserRepositories.current)
- .updateTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
- val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskMovingToFront(task)
-
- verify(desktopUserRepositories.current)
- .removeFreeformTask(task.displayId, task.taskId)
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
- val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
- whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
- .thenReturn(false)
-
- desktopTaskChangeListener.onTaskClosing(task)
-
- verify(desktopUserRepositories.current)
- .updateTask(task.displayId, task.taskId, isVisible = false)
- verify(desktopUserRepositories.current)
- .minimizeTask(task.displayId, task.taskId)
- }
-
- @Test
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
- val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
- whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskClosing(task)
-
- verify(desktopUserRepositories.current, never())
- .minimizeTask(task.displayId, task.taskId)
- verify(desktopUserRepositories.current)
- .removeClosingTask(task.taskId)
- verify(desktopUserRepositories.current)
- .removeFreeformTask(task.displayId, task.taskId)
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
- val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
- .thenReturn(true)
- whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
- .thenReturn(true)
-
- desktopTaskChangeListener.onTaskClosing(task)
-
- verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
- verify(desktopUserRepositories.current)
- .removeFreeformTask(task.displayId, task.taskId)
- }
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
+ val task = createFreeformTask().apply { isVisible = false }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+ val task = createFreeformTask().apply { isVisible = false }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskMovingToFront(task)
+
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskClosing(task)
+
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, isVisible = false)
+ verify(desktopUserRepositories.current).minimizeTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskClosing(task)
+
+ verify(desktopUserRepositories.current, never()).minimizeTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskClosing(task)
+
+ verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ }
}
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 c10434aa6d6f..0eb88e368054 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
@@ -21,6 +21,7 @@ import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.KeyguardManager
import android.app.PendingIntent
+import android.app.PictureInPictureParams
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -82,6 +83,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP
+import com.android.window.flags.Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY
import com.android.wm.shell.MockToken
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -94,7 +96,6 @@ import com.android.wm.shell.common.DisplayLayout
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.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -107,6 +108,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSplitScreenTask
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
import com.android.wm.shell.desktopmode.persistence.Desktop
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
@@ -135,8 +137,8 @@ import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import java.util.function.Consumer
import java.util.Optional
+import java.util.function.Consumer
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlin.test.assertIs
@@ -165,8 +167,8 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
@@ -187,4328 +189,4737 @@ import org.mockito.quality.Strictness
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
class DesktopTasksControllerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
- @Mock lateinit var testExecutor: ShellExecutor
- @Mock lateinit var shellCommandHandler: ShellCommandHandler
- @Mock lateinit var shellController: ShellController
- @Mock lateinit var displayController: DisplayController
- @Mock lateinit var displayLayout: DisplayLayout
- @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
- @Mock lateinit var syncQueue: SyncTransactionQueue
- @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
- @Mock lateinit var transitions: Transitions
- @Mock lateinit var keyguardManager: KeyguardManager
- @Mock lateinit var mReturnToDragStartAnimator: ReturnToDragStartAnimator
- @Mock lateinit var desktopMixedTransitionHandler: DesktopMixedTransitionHandler
- @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
- @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
- @Mock lateinit var dragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler
- @Mock
- lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
- @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
- @Mock
- lateinit var mMockDesktopImmersiveController: DesktopImmersiveController
- @Mock lateinit var splitScreenController: SplitScreenController
- @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
- @Mock lateinit var dragAndDropController: DragAndDropController
- @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
- @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
- @Mock lateinit var recentTasksController: RecentTasksController
- @Mock
- private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
- @Mock private lateinit var mockSurface: SurfaceControl
- @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
- @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
- @Mock private lateinit var mockHandler: Handler
- @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger
- @Mock private lateinit var desktopModeUiEventLogger: DesktopModeUiEventLogger
- @Mock lateinit var persistentRepository: DesktopPersistentRepository
- @Mock lateinit var motionEvent: MotionEvent
- @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
- @Mock private lateinit var mockToast: Toast
- private lateinit var mockitoSession: StaticMockitoSession
- @Mock
- private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
- @Mock
- private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
- @Mock private lateinit var resources: Resources
- @Mock
- lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
- @Mock private lateinit var userManager: UserManager
- private lateinit var controller: DesktopTasksController
- private lateinit var shellInit: ShellInit
- private lateinit var taskRepository: DesktopRepository
- private lateinit var userRepositories: DesktopUserRepositories
- private lateinit var desktopTasksLimiter: DesktopTasksLimiter
- private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
- private lateinit var testScope: CoroutineScope
-
- private val shellExecutor = TestShellExecutor()
-
- // Mock running tasks are registered here so we can get the list from mock shell task organizer
- private val runningTasks = mutableListOf<RunningTaskInfo>()
-
- private val DISPLAY_DIMENSION_SHORT = 1600
- private val DISPLAY_DIMENSION_LONG = 2560
- private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
- private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085)
- private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635)
- private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275)
- private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611)
- private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275)
-
- @Before
- fun setUp() {
- Dispatchers.setMain(StandardTestDispatcher())
- mockitoSession =
- mockitoSession()
- .strictness(Strictness.LENIENT)
- .spyStatic(DesktopModeStatus::class.java)
- .spyStatic(Toast::class.java)
- .startMocking()
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-
- testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
- shellInit = spy(ShellInit(testExecutor))
- userRepositories =
- DesktopUserRepositories(
- context,
- shellInit,
- persistentRepository,
- repositoryInitializer,
- testScope,
- userManager)
- desktopTasksLimiter =
- DesktopTasksLimiter(
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var shellCommandHandler: ShellCommandHandler
+ @Mock lateinit var shellController: ShellController
+ @Mock lateinit var displayController: DisplayController
+ @Mock lateinit var displayLayout: DisplayLayout
+ @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock lateinit var syncQueue: SyncTransactionQueue
+ @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock lateinit var transitions: Transitions
+ @Mock lateinit var keyguardManager: KeyguardManager
+ @Mock lateinit var mReturnToDragStartAnimator: ReturnToDragStartAnimator
+ @Mock lateinit var desktopMixedTransitionHandler: DesktopMixedTransitionHandler
+ @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
+ @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
+ @Mock lateinit var dragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler
+ @Mock
+ lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
+ @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
+ @Mock lateinit var mMockDesktopImmersiveController: DesktopImmersiveController
+ @Mock lateinit var splitScreenController: SplitScreenController
+ @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
+ @Mock lateinit var dragAndDropController: DragAndDropController
+ @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
+ @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
+ @Mock lateinit var recentTasksController: RecentTasksController
+ @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ @Mock private lateinit var mockSurface: SurfaceControl
+ @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
+ @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
+ @Mock private lateinit var mockHandler: Handler
+ @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+ @Mock private lateinit var desktopModeUiEventLogger: DesktopModeUiEventLogger
+ @Mock lateinit var persistentRepository: DesktopPersistentRepository
+ @Mock lateinit var motionEvent: MotionEvent
+ @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
+ @Mock private lateinit var mockToast: Toast
+ private lateinit var mockitoSession: StaticMockitoSession
+ @Mock private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+ @Mock private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
+ @Mock private lateinit var resources: Resources
+ @Mock
+ lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
+ @Mock private lateinit var userManager: UserManager
+ private lateinit var controller: DesktopTasksController
+ private lateinit var shellInit: ShellInit
+ private lateinit var taskRepository: DesktopRepository
+ private lateinit var userRepositories: DesktopUserRepositories
+ private lateinit var desktopTasksLimiter: DesktopTasksLimiter
+ private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
+ private lateinit var testScope: CoroutineScope
+
+ private val shellExecutor = TestShellExecutor()
+
+ // Mock running tasks are registered here so we can get the list from mock shell task organizer
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+
+ private val DISPLAY_DIMENSION_SHORT = 1600
+ private val DISPLAY_DIMENSION_LONG = 2560
+ private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
+ private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085)
+ private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635)
+ private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275)
+ private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611)
+ private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275)
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(StandardTestDispatcher())
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .spyStatic(Toast::class.java)
+ .startMocking()
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+ testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+ shellInit = spy(ShellInit(testExecutor))
+ userRepositories =
+ DesktopUserRepositories(
+ context,
+ shellInit,
+ shellController,
+ persistentRepository,
+ repositoryInitializer,
+ testScope,
+ userManager,
+ )
+ desktopTasksLimiter =
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ MAX_TASK_LIMIT,
+ mockInteractionJankMonitor,
+ mContext,
+ mockHandler,
+ )
+
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+ whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
+ whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+ .thenReturn(Desktop.getDefaultInstance())
+ doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
+
+ val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ any<RunningTaskInfo>(),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.NoExit)
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ anyInt(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.NoExit)
+
+ controller = createController()
+ controller.setSplitScreenController(splitScreenController)
+ controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
+ controller.desktopModeEnterExitTransitionListener = desktopModeEnterExitTransitionListener
+
+ shellInit.init()
+
+ val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
+ verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
+ recentsTransitionStateListener = captor.value
+
+ controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
+
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ taskRepository = userRepositories.current
+ }
+
+ private fun createController(): DesktopTasksController {
+ return DesktopTasksController(
+ context,
+ shellInit,
+ shellCommandHandler,
+ shellController,
+ displayController,
+ shellTaskOrganizer,
+ syncQueue,
+ rootTaskDisplayAreaOrganizer,
+ dragAndDropController,
transitions,
+ keyguardManager,
+ mReturnToDragStartAnimator,
+ desktopMixedTransitionHandler,
+ enterDesktopTransitionHandler,
+ exitDesktopTransitionHandler,
+ dragAndDropTransitionHandler,
+ toggleResizeDesktopTaskTransitionHandler,
+ dragToDesktopTransitionHandler,
+ mMockDesktopImmersiveController,
userRepositories,
- shellTaskOrganizer,
- MAX_TASK_LIMIT,
+ recentsTransitionHandler,
+ multiInstanceHelper,
+ shellExecutor,
+ Optional.of(desktopTasksLimiter),
+ recentTasksController,
mockInteractionJankMonitor,
- mContext,
- mockHandler)
-
- whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
- whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
- whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
- whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
- }
- whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
- Desktop.getDefaultInstance()
- )
- doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
-
- val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>(), any()))
- .thenReturn(ExitResult.NoExit)
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
- .thenReturn(ExitResult.NoExit)
-
- controller = createController()
- controller.setSplitScreenController(splitScreenController)
- controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
- controller.desktopModeEnterExitTransitionListener = desktopModeEnterExitTransitionListener
-
- shellInit.init()
-
- val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
- verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
- recentsTransitionStateListener = captor.value
-
- controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
-
- assumeTrue(ENABLE_SHELL_TRANSITIONS)
-
- taskRepository = userRepositories.current
- }
-
- private fun createController(): DesktopTasksController {
- return DesktopTasksController(
- context,
- shellInit,
- shellCommandHandler,
- shellController,
- displayController,
- shellTaskOrganizer,
- syncQueue,
- rootTaskDisplayAreaOrganizer,
- dragAndDropController,
- transitions,
- keyguardManager,
- mReturnToDragStartAnimator,
- desktopMixedTransitionHandler,
- enterDesktopTransitionHandler,
- exitDesktopTransitionHandler,
- dragAndDropTransitionHandler,
- toggleResizeDesktopTaskTransitionHandler,
- dragToDesktopTransitionHandler,
- mMockDesktopImmersiveController,
- userRepositories,
- recentsTransitionHandler,
- multiInstanceHelper,
- shellExecutor,
- Optional.of(desktopTasksLimiter),
- recentTasksController,
- mockInteractionJankMonitor,
- mockHandler,
- desktopModeEventLogger,
- desktopModeUiEventLogger,
- desktopTilingDecorViewModel,
- )
- }
-
- @After
- fun tearDown() {
- mockitoSession.finishMocking()
-
- runningTasks.clear()
- testScope.cancel()
- }
-
- @Test
- fun instantiate_addInitCallback() {
- verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
- }
-
- @Test
- fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
- setUpFreeformTask()
-
- assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
- }
-
- @Test
- fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
- val task1 = setUpFreeformTask()
-
- val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
- controller.toggleDesktopTaskSize(
- task1,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
+ mockHandler,
+ desktopModeEventLogger,
+ desktopModeUiEventLogger,
+ desktopTilingDecorViewModel,
+ )
+ }
- verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task1,
- STABLE_BOUNDS.width(),
- STABLE_BOUNDS.height(),
- displayController
- )
- assertThat(argumentCaptor.value).isTrue()
- }
-
- @Test
- fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
- val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
- setUpFreeformTask(bounds = stableBounds, active = true)
- assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
- }
-
- @Test
- fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfMaximizedTask_returnFalse() {
- val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
- val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
-
- val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
- controller.toggleDesktopTaskSize(
- task1,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.RESTORE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
- InputMethod.TOUCH
- )
- )
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
- verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- eq(ResizeTrigger.MAXIMIZE_BUTTON),
- eq(InputMethod.TOUCH),
- eq(task1),
- anyOrNull(),
- anyOrNull(),
- eq(displayController),
- anyOrNull()
- )
- assertThat(argumentCaptor.value).isFalse()
- }
-
- @Test
- fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
- val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
- setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
-
- assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
- }
-
-
- @Test
- fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
- whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
- clearInvocations(shellInit)
-
- createController()
-
- verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>())
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
- val homeTask = setUpHomeTask()
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskHidden(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: home, task1, task2
- wct.assertReorderAt(index = 0, homeTask)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskHidden(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: wallpaper intent, task1, task2
- wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- fun isDesktopModeShowing_noTasks_returnsFalse() {
- assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
- }
-
- @Test
- fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskHidden(task2)
-
- assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
- }
-
- @Test
- fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskVisible(task1)
- markTaskHidden(task2)
-
- assertThat(controller.isDesktopModeShowing(displayId = 0)).isTrue()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
- val homeTask = setUpHomeTask(SECOND_DISPLAY)
- val task1 = setUpFreeformTask(SECOND_DISPLAY)
- val task2 = setUpFreeformTask(SECOND_DISPLAY)
- markTaskHidden(task1)
- markTaskHidden(task2)
-
- controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: home, task1, task2 (no wallpaper intent)
- wct.assertReorderAt(index = 0, homeTask)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
- val homeTask = setUpHomeTask()
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskVisible(task1)
- markTaskVisible(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: home, task1, task2
- wct.assertReorderAt(index = 0, homeTask)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() {
- val homeTask = setUpHomeTask(SECOND_DISPLAY)
- val task1 = setUpFreeformTask(SECOND_DISPLAY)
- val task2 = setUpFreeformTask(SECOND_DISPLAY)
- markTaskHidden(task1)
- markTaskHidden(task2)
-
- controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: home, task1, task2
- wct.assertReorderAt(index = 0, homeTask)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskVisible(task1)
- markTaskVisible(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: wallpaper intent, task1, task2
- wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
- val homeTask = setUpHomeTask()
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskVisible(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: home, task1, task2
- wct.assertReorderAt(index = 0, homeTask)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskVisible(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: wallpaper intent, task1, task2
- wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
- val homeTask = setUpHomeTask()
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(1)
- wct.assertReorderAt(index = 0, homeTask)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
- val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
- val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
- setUpHomeTask(SECOND_DISPLAY)
- val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
- markTaskHidden(taskDefaultDisplay)
- markTaskHidden(taskSecondDisplay)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(2)
- // Expect order to be from bottom: home, task
- wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
- wct.assertReorderAt(index = 1, taskDefaultDisplay)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
- val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
- val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
- setUpHomeTask(SECOND_DISPLAY)
- val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
- markTaskHidden(taskDefaultDisplay)
- markTaskHidden(taskSecondDisplay)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Move home to front
- wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
- // Add desktop wallpaper activity
- wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
- // Move freeform task to front
- wct.assertReorderAt(index = 2, taskDefaultDisplay)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- val minimizedTask = setUpFreeformTask()
-
- markTaskHidden(freeformTask)
- markTaskHidden(minimizedTask)
- taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(2)
- // Reorder home and freeform task to top, don't reorder the minimized task
- wct.assertReorderAt(index = 0, homeTask, toTop = true)
- wct.assertReorderAt(index = 1, freeformTask, toTop = true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- val minimizedTask = setUpFreeformTask()
-
- markTaskHidden(freeformTask)
- markTaskHidden(minimizedTask)
- taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Move home to front
- wct.assertReorderAt(index = 0, homeTask, toTop = true)
- // Add desktop wallpaper activity
- wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
- // Reorder freeform task to top, don't reorder the minimized task
- wct.assertReorderAt(index = 2, freeformTask, toTop = true)
- }
-
- @Test
- fun visibleTaskCount_noTasks_returnsZero() {
- assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
- }
-
- @Test
- fun visibleTaskCount_twoTasks_bothVisible_returnsTwo() {
- setUpHomeTask()
- setUpFreeformTask().also(::markTaskVisible)
- setUpFreeformTask().also(::markTaskVisible)
- assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
- }
-
- @Test
- fun visibleTaskCount_twoTasks_oneVisible_returnsOne() {
- setUpHomeTask()
- setUpFreeformTask().also(::markTaskVisible)
- setUpFreeformTask().also(::markTaskHidden)
- assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
- }
-
- @Test
- fun visibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
- setUpHomeTask()
- setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
- setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
- assertThat(controller.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
- }
-
- @Test
- fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(gravity = Gravity.LEFT)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
- }
-
- @Test
- fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
- }
-
- @Test
- fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(gravity = Gravity.TOP)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
- }
-
- @Test
- fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
- val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
-
- val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
- assertNotNull(wct, "should handle request")
- val finalBounds = findBoundsChange(wct, freeformTask)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.BottomRight)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
- val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-
- val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
- assertNull(wct, "should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionBottomRight() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.BottomRight)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionTopLeft() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds)
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.TopLeft)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionBottomLeft() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds)
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.BottomLeft)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionTopRight() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds)
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.TopRight)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionResetsToCenter() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds)
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.Center)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowSnapLeft_positionResetsToCenter() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- // Add freeform task with half display size snap bounds at left side.
- setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.Center)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowSnapRight_positionResetsToCenter() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- // Add freeform task with half display size snap bounds at right side.
- setUpFreeformTask(bounds = Rect(
- stableBounds.right - 500, stableBounds.top, stableBounds.right, stableBounds.bottom))
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.Center)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowMaximised_positionResetsToCenter() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- // Add maximised freeform task.
- setUpFreeformTask(bounds = Rect(stableBounds))
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.Center)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_defaultToCenterIfFree() {
- setUpLandscapeDisplay()
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- val minTouchTarget = context.resources.getDimensionPixelSize(
- R.dimen.freeform_required_visible_empty_space_in_header)
- addFreeformTaskAtPosition(DesktopTaskPosition.Center, stableBounds,
- Rect(0, 0, 1600, 1200), Point(0, minTouchTarget + 1))
-
- val task = setUpFullscreenTask()
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- val finalBounds = findBoundsChange(wct, task)
- assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
- .isEqualTo(DesktopTaskPosition.Center)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
- shouldLetterbox = true, aspectRatioOverrideApplied = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
- setUpPortraitDisplay()
- val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
- setUpPortraitDisplay()
- val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
- setUpPortraitDisplay()
- val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
- deviceOrientation = ORIENTATION_PORTRAIT,
- shouldLetterbox = true, aspectRatioOverrideApplied = true)
- val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
-
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
- }
-
- @Test
- fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
- val task = setUpFullscreenTask()
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- fun moveRunningTaskToDesktop_tdaFreeform_windowingModeSetToUndefined() {
- val task = setUpFullscreenTask()
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- fun moveTaskToDesktop_nonExistentTask_doesNothing() {
- controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
- verifyEnterDesktopWCTNotExecuted()
- verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(anyInt())
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
- val task = createTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
-
- controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
-
- with(getLatestEnterDesktopWct()) {
- assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
- val task = createTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
-
- controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
-
- with(getLatestEnterDesktopWct()) {
- // Add desktop wallpaper activity
- assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- // Launch task
- assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
- val task =
- setUpFullscreenTask().apply {
- isTopActivityTransparent = true
- isTopActivityNoDisplay = true
- numActivities = 1
- }
-
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
- val task =
- setUpFullscreenTask().apply {
- isTopActivityTransparent = true
- isTopActivityNoDisplay = false
- numActivities = 1
- }
-
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- verifyEnterDesktopWCTNotExecuted()
- verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(
- FREEFORM_ANIMATION_DURATION
+ runningTasks.clear()
+ testScope.cancel()
+ }
+
+ @Test
+ fun instantiate_addInitCallback() {
+ verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
+ setUpFreeformTask()
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
+ val task1 = setUpFreeformTask()
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(
+ task1,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task1,
+ STABLE_BOUNDS.width(),
+ STABLE_BOUNDS.height(),
+ displayController,
+ )
+ assertThat(argumentCaptor.value).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(bounds = stableBounds, active = true)
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfMaximizedTask_returnFalse() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(
+ task1,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ eq(ResizeTrigger.MAXIMIZE_BUTTON),
+ eq(InputMethod.TOUCH),
+ eq(task1),
+ anyOrNull(),
+ anyOrNull(),
+ eq(displayController),
+ anyOrNull(),
+ )
+ assertThat(argumentCaptor.value).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(
+ bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)
+ )
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+ clearInvocations(shellInit)
+
+ createController()
+
+ verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ fun isDesktopModeShowing_noTasks_returnsFalse() {
+ assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+ }
+
+ @Test
+ fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+ }
+
+ @Test
+ fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskVisible(task1)
+ markTaskHidden(task2)
+
+ assertThat(controller.isDesktopModeShowing(displayId = 0)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
+ val homeTask = setUpHomeTask(SECOND_DISPLAY)
+ val task1 = setUpFreeformTask(SECOND_DISPLAY)
+ val task2 = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2 (no wallpaper intent)
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskVisible(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() {
+ val homeTask = setUpHomeTask(SECOND_DISPLAY)
+ val task1 = setUpFreeformTask(SECOND_DISPLAY)
+ val task2 = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskVisible(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
+ val homeTask = setUpHomeTask()
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, homeTask)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
+ val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+ val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+ setUpHomeTask(SECOND_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(taskDefaultDisplay)
+ markTaskHidden(taskSecondDisplay)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Expect order to be from bottom: home, task
+ wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+ wct.assertReorderAt(index = 1, taskDefaultDisplay)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+ val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+ val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+ setUpHomeTask(SECOND_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(taskDefaultDisplay)
+ markTaskHidden(taskSecondDisplay)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Move home to front
+ wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+ // Move freeform task to front
+ wct.assertReorderAt(index = 2, taskDefaultDisplay)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val minimizedTask = setUpFreeformTask()
+
+ markTaskHidden(freeformTask)
+ markTaskHidden(minimizedTask)
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Reorder home and freeform task to top, don't reorder the minimized task
+ wct.assertReorderAt(index = 0, homeTask, toTop = true)
+ wct.assertReorderAt(index = 1, freeformTask, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val minimizedTask = setUpFreeformTask()
+
+ markTaskHidden(freeformTask)
+ markTaskHidden(minimizedTask)
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Move home to front
+ wct.assertReorderAt(index = 0, homeTask, toTop = true)
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+ // Reorder freeform task to top, don't reorder the minimized task
+ wct.assertReorderAt(index = 2, freeformTask, toTop = true)
+ }
+
+ @Test
+ fun visibleTaskCount_noTasks_returnsZero() {
+ assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ }
+
+ @Test
+ fun visibleTaskCount_twoTasks_bothVisible_returnsTwo() {
+ setUpHomeTask()
+ setUpFreeformTask().also(::markTaskVisible)
+ setUpFreeformTask().also(::markTaskVisible)
+ assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
+ }
+
+ @Test
+ fun visibleTaskCount_twoTasks_oneVisible_returnsOne() {
+ setUpHomeTask()
+ setUpFreeformTask().also(::markTaskVisible)
+ setUpFreeformTask().also(::markTaskHidden)
+ assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ }
+
+ @Test
+ fun visibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ setUpHomeTask()
+ setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
+ setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
+ assertThat(controller.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+ }
+
+ @Test
+ fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(gravity = Gravity.LEFT)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(finalBounds).isEqualTo(Rect())
+ }
+
+ @Test
+ fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(finalBounds).isEqualTo(Rect())
+ }
+
+ @Test
+ fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(gravity = Gravity.TOP)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(finalBounds).isEqualTo(Rect())
+ }
+
+ @Test
+ fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(finalBounds).isEqualTo(Rect())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+ val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
+
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNotNull(wct, "should handle request")
+ val finalBounds = findBoundsChange(wct, freeformTask)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.BottomRight)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+ val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNull(wct, "should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_positionBottomRight() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.BottomRight)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_positionTopLeft() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds)
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.TopLeft)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_positionBottomLeft() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds)
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.BottomLeft)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_positionTopRight() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds)
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.TopRight)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_positionResetsToCenter() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds)
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_lastWindowSnapLeft_positionResetsToCenter() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ // Add freeform task with half display size snap bounds at left side.
+ setUpFreeformTask(
+ bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)
+ )
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_lastWindowSnapRight_positionResetsToCenter() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ // Add freeform task with half display size snap bounds at right side.
+ setUpFreeformTask(
+ bounds =
+ Rect(
+ stableBounds.right - 500,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom,
+ )
+ )
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_lastWindowMaximised_positionResetsToCenter() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ // Add maximised freeform task.
+ setUpFreeformTask(bounds = Rect(stableBounds))
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun addMoveToDesktopChanges_defaultToCenterIfFree() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ val minTouchTarget =
+ context.resources.getDimensionPixelSize(
+ R.dimen.freeform_required_visible_empty_space_in_header
+ )
+ addFreeformTaskAtPosition(
+ DesktopTaskPosition.Center,
+ stableBounds,
+ Rect(0, 0, 1600, 1200),
+ Point(0, minTouchTarget + 1),
+ )
+
+ val task = setUpFullscreenTask()
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
+ setUpLandscapeDisplay()
+ val task =
+ setUpFullscreenTask(
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ shouldLetterbox = true,
+ aspectRatioOverrideApplied = true,
+ )
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
+ setUpPortraitDisplay()
+ val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
+ setUpPortraitDisplay()
+ val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
+ setUpPortraitDisplay()
+ val task =
+ setUpFullscreenTask(
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ shouldLetterbox = true,
+ aspectRatioOverrideApplied = true,
+ )
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
+ val task = setUpFullscreenTask()
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_tdaFreeform_windowingModeSetToUndefined() {
+ val task = setUpFullscreenTask()
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveTaskToDesktop_nonExistentTask_doesNothing() {
+ controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
+ verifyEnterDesktopWCTNotExecuted()
+ verify(desktopModeEnterExitTransitionListener, times(0))
+ .onEnterDesktopModeTransitionStarted(anyInt())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+ controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+ controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ // Add desktop wallpaper activity
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Launch task
+ assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
+ val task =
+ setUpFullscreenTask().apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = true
+ numActivities = 1
+ }
+
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
+ val task =
+ setUpFullscreenTask().apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ }
+
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ verifyEnterDesktopWCTNotExecuted()
+ verify(desktopModeEnterExitTransitionListener, times(0))
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
+
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ verifyEnterDesktopWCTNotExecuted()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing() {
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }
+
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
+ val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+ whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+ .thenReturn(Binder())
+
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ controller.moveTaskToDesktop(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
+ val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+ whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+ .thenReturn(Binder())
+
+ controller.moveRunningTaskToDesktop(
+ task = setUpFullscreenTask(),
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val fullscreenTask = setUpFullscreenTask()
+ markTaskHidden(freeformTask)
+
+ controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ // Operations should include home task, freeform task
+ assertThat(hierarchyOps).hasSize(3)
+ assertReorderSequence(homeTask, freeformTask, fullscreenTask)
+ assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+ val freeformTask = setUpFreeformTask()
+ val fullscreenTask = setUpFullscreenTask()
+ markTaskHidden(freeformTask)
+
+ controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ // Operations should include wallpaper intent, freeform task, fullscreen task
+ assertThat(hierarchyOps).hasSize(3)
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ assertReorderAt(index = 1, freeformTask)
+ assertReorderAt(index = 2, fullscreenTask)
+ assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
+ setUpHomeTask(displayId = DEFAULT_DISPLAY)
+ val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ markTaskHidden(freeformTaskDefault)
+
+ val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ markTaskHidden(freeformTaskSecond)
+
+ controller.moveRunningTaskToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ // Check that hierarchy operations do not include tasks from second display
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(homeTaskSecond.token.asBinder())
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(freeformTaskSecond.token.asBinder())
+ }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_splitTaskExitsSplit() {
+ val task = setUpSplitScreenTask()
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ verify(splitScreenController)
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
+ val task = setUpFullscreenTask()
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ verify(splitScreenController, never())
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ val newTask = setUpFullscreenTask()
+ val homeTask = setUpHomeTask()
+
+ controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
+ wct.assertReorderAt(0, homeTask)
+ wct.assertReorderSequenceInRange(
+ range = 1..<(MAX_TASK_LIMIT + 1),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ val newTask = setUpFullscreenTask()
+ val homeTask = setUpHomeTask()
+
+ controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
+ // Move home to front
+ wct.assertReorderAt(0, homeTask)
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(1, desktopWallpaperIntent)
+ // Bring freeform tasks to front
+ wct.assertReorderSequenceInRange(
+ range = 2..<(MAX_TASK_LIMIT + 2),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask,
+ )
+ }
+
+ @Test
+ fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
+ val task = setUpFreeformTask()
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+ val wct = getLatestExitDesktopWct()
+ verify(desktopModeEnterExitTransitionListener, times(1))
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .configuration
+ .windowConfiguration
+ .windowingMode = WINDOWING_MODE_FULLSCREEN
+
+ controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
+ verify(desktopModeEnterExitTransitionListener)
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ // Removes wallpaper activity when leaving desktop
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
+ val task = setUpFreeformTask()
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+ val wct = getLatestExitDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ verify(desktopModeEnterExitTransitionListener)
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .configuration
+ .windowConfiguration
+ .windowingMode = WINDOWING_MODE_FREEFORM
+
+ controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
+ assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ verify(desktopModeEnterExitTransitionListener)
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ // Removes wallpaper activity when leaving desktop
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun moveToFullscreen_multipleVisibleNonMinimizedTasks_doesNotRemoveWallpaperActivity() {
+ val task1 = setUpFreeformTask()
+ // Setup task2
+ setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .configuration
+ .windowConfiguration
+ .windowingMode = WINDOWING_MODE_FULLSCREEN
+
+ controller.moveToFullscreen(task1.taskId, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ val task1Change = assertNotNull(wct.changes[task1.token.asBinder()])
+ assertThat(task1Change.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ verify(desktopModeEnterExitTransitionListener)
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ // Does not remove wallpaper activity, as desktop still has a visible desktop task
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun moveToFullscreen_nonExistentTask_doesNothing() {
+ controller.moveToFullscreen(999, transitionSource = UNKNOWN)
+ verifyExitDesktopWCTNotExecuted()
+ }
+
+ @Test
+ fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
+ val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
+
+ with(getLatestExitDesktopWct()) {
+ assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
+ assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
+ }
+ verify(desktopModeEnterExitTransitionListener)
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ }
+
+ @Test
+ fun moveTaskToFront_postsWctWithReorderOp() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ controller.moveTaskToFront(task1, remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, task1)
+ }
+
+ @Test
+ fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
+ setUpHomeTask()
+ val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(freeformTasks[0].taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
+ wct.assertReorderAt(0, freeformTasks[0], toTop = true)
+ wct.assertReorderAt(1, freeformTasks[1], toTop = false)
+ }
+
+ @Test
+ fun moveTaskToFront_remoteTransition_usesOneshotHandler() {
+ setUpHomeTask()
+ val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() }
+ val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+ whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+ }
+
+ @Test
+ fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
+ setUpHomeTask()
+ val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
+ val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+ whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+ assertThat(transitionHandlerArgCaptor.value)
+ .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java)
+ }
+
+ @Test
+ fun moveTaskToFront_backgroundTask_launchesTask() {
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+
+ controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ val task = createTaskInfo(1001)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize
+ wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ wct.assertReorderAt(1, freeformTasks[0], toTop = false)
+ }
+
+ @Test
+ fun moveToNextDisplay_noOtherDisplays() {
+ whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ controller.moveToNextDisplay(task.taskId)
+ verifyWCTNotExecuted()
+ }
+
+ @Test
+ fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ controller.moveToNextDisplay(task.taskId)
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
+ assertThat(hierarchyOps).hasSize(1)
+ assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
+ assertThat(hierarchyOps[0].isReparent).isTrue()
+ assertThat(hierarchyOps[0].newParent).isEqualTo(secondDisplayArea.token.asBinder())
+ assertThat(hierarchyOps[0].toTop).isTrue()
+ }
+ }
+
+ @Test
+ fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: default display
+ val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultDisplayArea)
+
+ val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ controller.moveToNextDisplay(task.taskId)
+
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
+ assertThat(hierarchyOps).hasSize(1)
+ assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
+ assertThat(hierarchyOps[0].isReparent).isTrue()
+ assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder())
+ assertThat(hierarchyOps[0].toTop).isTrue()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY)
+ fun moveToNextDisplay_removeWallpaper() {
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ // Add a task and a wallpaper
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ controller.moveToNextDisplay(task.taskId)
+
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
+ val wallpaperChange =
+ hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() }
+ assertThat(wallpaperChange).isNotNull()
+ assertThat(wallpaperChange!!.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ }
+ }
+
+ @Test
+ fun getTaskWindowingMode() {
+ val fullscreenTask = setUpFullscreenTask()
+ val freeformTask = setUpFreeformTask()
+
+ assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId))
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(controller.getTaskWindowingMode(freeformTask.taskId))
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun onDesktopWindowClose_noActiveTasks() {
+ val task = setUpFreeformTask(active = false)
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
+ val task = setUpFreeformTask()
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_isClosing() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_isMinimized() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_multipleActiveTasks() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
+ val task = setUpFreeformTask(active = false)
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ controller.minimizeTask(task)
+
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ captor.value.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+ }
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_pipTask_autoEnterEnabled_startPipTransition() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+ val handler = mock(TransitionHandler::class.java)
+ whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(Binder())
+ whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
+ .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
+
+ controller.minimizeTask(task)
+
+ verify(freeformTaskTransitionStarter).startPipTransition(any())
+ verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_pipTask_autoEnterDisabled_startMinimizeTransition() {
+ val task = setUpPipTask(autoEnterEnabled = false)
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(Binder())
+
+ controller.minimizeTask(task)
+
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
+ verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
+ val task = setUpFreeformTask(active = true)
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+
+ controller.minimizeTask(task)
+
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ captor.value.hierarchyOps.none { hop -> hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK }
+ }
+
+ @Test
+ fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ // The only active task is being minimized.
+ controller.minimizeTask(task)
+
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ // Adds remove wallpaper operation
+ captor.value.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntRemoveWallpaper() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+
+ // The only active task is already minimized.
+ controller.minimizeTask(task)
+
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ captor.value.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+ }
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_multipleActiveTasks_doesntRemoveWallpaper() {
+ val task1 = setUpFreeformTask(active = true)
+ setUpFreeformTask(active = true)
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ controller.minimizeTask(task1)
+
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ captor.value.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+ }
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
+ val task1 = setUpFreeformTask(active = true)
+ val task2 = setUpFreeformTask(active = true)
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ val wallpaperToken = MockToken().token()
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+ // task1 is the only visible task as task2 is minimized.
+ controller.minimizeTask(task1)
+ // Adds remove wallpaper operation
+ val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+ // Adds remove wallpaper operation
+ captor.value.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_triesToExitImmersive() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+
+ controller.minimizeTask(task)
+
+ verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_invokesImmersiveTransitionStartCallback() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ val runOnTransit = RunOnStartTransitionCallback()
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(transition)
+ whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
+ .thenReturn(
+ ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
+ )
+
+ controller.minimizeTask(task)
+
+ assertThat(runOnTransit.invocations).isEqualTo(1)
+ assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+ assertThat(wct.hierarchyOps).hasSize(1)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+ // There are 5 hops that are happening in this case:
+ // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
+ // 2. Bringing home task to front
+ // 3. Pending intent for the wallpaper
+ // 4. Bringing the existing freeform task to top
+ // 5. Bringing the fullscreen task back at the top
+ assertThat(wct.hierarchyOps).hasSize(5)
+ wct.assertReorderAt(1, homeTask, toTop = true)
+ wct.assertReorderAt(4, fullscreenTask, toTop = true)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ // Make sure we only reorder the new task to top (we don't reorder the old task to bottom)
+ assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+ wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ // Make sure we reorder the new task to top, and the back task to the bottom
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
+ wct.assertReorderAt(0, fullscreenTask, toTop = true)
+ wct.assertReorderAt(1, freeformTasks[0], toTop = false)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ // Make sure we reorder the new task to top, and the back task to the bottom
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(9)
+ wct.assertReorderAt(0, fullscreenTask, toTop = true)
+ wct.assertReorderAt(8, freeformTasks[0], toTop = false)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
+ val minimizedTask = setUpFreeformTask()
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val homeTask = setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
+ wct.assertReorderAt(0, fullscreenTask, toTop = true)
+ // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
+ // task is under the home task.
+ wct.assertReorderAt(1, homeTask, toTop = true)
+ wct.assertReorderAt(9, freeformTasks[0], toTop = false)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+
+ val fullscreenTask = createFullscreenTask()
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // There are 3 hops that are happening in this case:
+ // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
+ // 2. Pending intent for the wallpaper
+ // 3. Bringing the fullscreen task back at the top
+ wct.assertPendingIntentAt(1, desktopWallpaperIntent)
+ wct.assertReorderAt(2, fullscreenTask, toTop = true)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+
+ val fullscreenTask = createFullscreenTask()
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertThat(wct).isNull()
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
+ val freeformTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
+ val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val newFreeformTask = createFreeformTask()
+
+ val wct =
+ controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
+
+ assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+ wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
+ }
+
+ @Test
+ fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
+ val freeformTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+
+ val freeformTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNotNull(wct, "should handle request")
+ assertFalse(wct.anyWindowingModeChange(freeformTask.token))
+ }
+
+ @Test
+ fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+
+ val freeformTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
+ val freeformTask1 = setUpFreeformTask()
+ val freeformTask2 = createFreeformTask()
+
+ markTaskHidden(freeformTask1)
+ val result =
+ controller.handleRequest(
+ Binder(),
+ createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+ )
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ result.assertReorderAt(1, freeformTask2, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+ val freeformTask1 = setUpFreeformTask()
+ val freeformTask2 = createFreeformTask()
+
+ markTaskHidden(freeformTask1)
+ val result =
+ controller.handleRequest(
+ Binder(),
+ createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+ )
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(3)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring active desktop tasks to front
+ result.assertReorderAt(1, freeformTask1, toTop = true)
+ // Bring new task to front
+ result.assertReorderAt(2, freeformTask2, toTop = true)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
+ val task = createFreeformTask()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, task, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
+ val task = createFreeformTask()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, task, toTop = true)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
+ }
+
+ @Test
+ fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
+ whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false)
+
+ val freeformTask1 = setUpFreeformTask()
+ markTaskVisible(freeformTask1)
+
+ val freeformTask2 = createFreeformTask()
+ val result =
+ controller.handleRequest(
+ freeformTask2.token.asBinder(),
+ createTransition(freeformTask2),
+ )
+ assertFalse(result.anyDensityConfigChange(freeformTask2.token))
+ }
+
+ @Test
+ fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
+ whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true)
+
+ val freeformTask1 = setUpFreeformTask()
+ markTaskVisible(freeformTask1)
+
+ val freeformTask2 = createFreeformTask()
+ val result =
+ controller.handleRequest(
+ freeformTask2.token.asBinder(),
+ createTransition(freeformTask2),
+ )
+ assertTrue(result.anyDensityConfigChange(freeformTask2.token))
+ }
+
+ @Test
+ fun handleRequest_freeformTask_keyguardLocked_returnNull() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNull(result, "Should NOT handle request")
+ }
+
+ @Test
+ fun handleRequest_notOpenOrToFrontTransition_returnNull() {
+ val task =
+ TestRunningTaskInfoBuilder()
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build()
+ val transition = createTransition(task = task, type = TRANSIT_CLOSE)
+ val result = controller.handleRequest(Binder(), transition)
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun handleRequest_noTriggerTask_returnNull() {
+ assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
+ }
+
+ @Test
+ fun handleRequest_triggerTaskNotStandard_returnNull() {
+ val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+ assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+ }
+
+ @Test
+ fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
+ val task =
+ TestRunningTaskInfoBuilder()
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .build()
+ assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+ }
+
+ @Test
+ fun handleRequest_recentsAnimationRunning_returnNull() {
+ // Set up a visible freeform task so a fullscreen task should be converted to freeform
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Mark recents animation running
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
+
+ // Open a fullscreen task, check that it does not result in a WCT with changes to it
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ fun handleRequest_recentsAnimationRunning_relaunchActiveTask_taskBecomesUndefined() {
+ // Set up a visible freeform task
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Mark recents animation running
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
+
+ // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
+ val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+ assertThat(result?.changes?.get(freeformTask.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ val task =
+ setUpFullscreenTask().apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = true
+ numActivities = 1
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ val task =
+ setUpFreeformTask().apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFreeformTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
+ val task = setUpFreeformTask()
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskNoToken_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleTasks_noWallpaper_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleTasks_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
- // Set task as systemUI package
- val systemUIPackageName = context.resources.getString(
- com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- val task =
- setUpFullscreenTask().apply {
- baseActivity = baseComponent
- isTopActivityNoDisplay = false
- }
-
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- verifyEnterDesktopWCTNotExecuted()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing() {
- // Set task as systemUI package
- val systemUIPackageName = context.resources.getString(
- com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- val task =
- setUpFullscreenTask().apply {
- baseActivity = baseComponent
- isTopActivityNoDisplay = true
- }
-
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- fun moveRunningTaskToDesktop_deviceSupported_taskIsMovedToDesktop() {
- val task = setUpFullscreenTask()
-
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- val fullscreenTask = setUpFullscreenTask()
- markTaskHidden(freeformTask)
-
- controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
-
- with(getLatestEnterDesktopWct()) {
- // Operations should include home task, freeform task
- assertThat(hierarchyOps).hasSize(3)
- assertReorderSequence(homeTask, freeformTask, fullscreenTask)
- assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- }
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
- val freeformTask = setUpFreeformTask()
- val fullscreenTask = setUpFullscreenTask()
- markTaskHidden(freeformTask)
-
- controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
-
- with(getLatestEnterDesktopWct()) {
- // Operations should include wallpaper intent, freeform task, fullscreen task
- assertThat(hierarchyOps).hasSize(3)
- assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- assertReorderAt(index = 1, freeformTask)
- assertReorderAt(index = 2, fullscreenTask)
- assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- }
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
- setUpHomeTask(displayId = DEFAULT_DISPLAY)
- val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
- markTaskHidden(freeformTaskDefault)
-
- val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
- val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
- markTaskHidden(freeformTaskSecond)
-
- controller.moveRunningTaskToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
-
- with(getLatestEnterDesktopWct()) {
- // Check that hierarchy operations do not include tasks from second display
- assertThat(hierarchyOps.map { it.container }).doesNotContain(homeTaskSecond.token.asBinder())
- assertThat(hierarchyOps.map { it.container })
- .doesNotContain(freeformTaskSecond.token.asBinder())
- }
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
-
- @Test
- fun moveRunningTaskToDesktop_splitTaskExitsSplit() {
- val task = setUpSplitScreenTask()
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- verify(splitScreenController)
- .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
- }
-
- @Test
- fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
- val task = setUpFullscreenTask()
- controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- verify(splitScreenController, never())
- .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- val newTask = setUpFullscreenTask()
- val homeTask = setUpHomeTask()
-
- controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
- wct.assertReorderAt(0, homeTask)
- wct.assertReorderSequenceInRange(
- range = 1..<(MAX_TASK_LIMIT + 1),
- *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
- newTask)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- val newTask = setUpFullscreenTask()
- val homeTask = setUpHomeTask()
-
- controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
- // Move home to front
- wct.assertReorderAt(0, homeTask)
- // Add desktop wallpaper activity
- wct.assertPendingIntentAt(1, desktopWallpaperIntent)
- // Bring freeform tasks to front
- wct.assertReorderSequenceInRange(
- range = 2..<(MAX_TASK_LIMIT + 2),
- *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
- newTask)
- }
-
- @Test
- fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
- val task = setUpFreeformTask()
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
- val wct = getLatestExitDesktopWct()
- verify(desktopModeEnterExitTransitionListener, times(1)).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
- controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
- verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
- // Removes wallpaper activity when leaving desktop
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
- val task = setUpFreeformTask()
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
- val wct = getLatestExitDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- }
-
- @Test
- fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
- controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
- assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
- verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- // Removes wallpaper activity when leaving desktop
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun moveToFullscreen_multipleVisibleNonMinimizedTasks_doesNotRemoveWallpaperActivity() {
- val task1 = setUpFreeformTask()
- // Setup task2
- setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
- controller.moveToFullscreen(task1.taskId, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- val task1Change = assertNotNull(wct.changes[task1.token.asBinder()])
- assertThat(task1Change.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
- verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- // Does not remove wallpaper activity, as desktop still has a visible desktop task
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun moveToFullscreen_nonExistentTask_doesNothing() {
- controller.moveToFullscreen(999, transitionSource = UNKNOWN)
- verifyExitDesktopWCTNotExecuted()
- }
-
- @Test
- fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
- val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
- controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
-
- with(getLatestExitDesktopWct()) {
- assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
- assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
- }
- verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- }
-
- @Test
- fun moveTaskToFront_postsWctWithReorderOp() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
-
- controller.moveTaskToFront(task1, remoteTransition = null)
-
- val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
- assertThat(wct.hierarchyOps).hasSize(1)
- wct.assertReorderAt(index = 0, task1)
- }
-
- @Test
- fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
- setUpHomeTask()
- val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
- whenever(desktopMixedTransitionHandler.startLaunchTransition(
- eq(TRANSIT_TO_FRONT),
- any(),
- eq(freeformTasks[0].taskId),
- anyOrNull(),
- anyOrNull(),
- )).thenReturn(Binder())
-
- controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
-
- val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
- assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
- wct.assertReorderAt(0, freeformTasks[0], toTop = true)
- wct.assertReorderAt(1, freeformTasks[1], toTop = false)
- }
-
- @Test
- fun moveTaskToFront_remoteTransition_usesOneshotHandler() {
- setUpHomeTask()
- val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() }
- val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
- whenever(
- transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
- ).thenReturn(Binder())
-
- controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
-
- assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
- }
-
- @Test
- fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
- setUpHomeTask()
- val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
- val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
- whenever(
- transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
- ).thenReturn(Binder())
-
- controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
-
- assertThat(transitionHandlerArgCaptor.value)
- .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java)
- }
-
- @Test
- fun moveTaskToFront_backgroundTask_launchesTask() {
- val task = createTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-
- controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
- val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
- assertThat(wct.hierarchyOps).hasSize(1)
- wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- val task = createTaskInfo(1001)
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
- whenever(desktopMixedTransitionHandler
- .startLaunchTransition(eq(TRANSIT_OPEN), any(), eq(task.taskId), anyOrNull(), anyOrNull()))
- .thenReturn(Binder())
-
- controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
- val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
- assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize
- wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
- wct.assertReorderAt(1, freeformTasks[0], toTop = false)
- }
-
- @Test
- fun moveToNextDisplay_noOtherDisplays() {
- whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
- val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- controller.moveToNextDisplay(task.taskId)
- verifyWCTNotExecuted()
- }
-
- @Test
- fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
- // Set up two display ids
- whenever(rootTaskDisplayAreaOrganizer.displayIds)
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
- // Create a mock for the target display area: second display
- val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
- .thenReturn(secondDisplayArea)
-
- val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- assertThat(hierarchyOps).hasSize(1)
- assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
- assertThat(hierarchyOps[0].isReparent).isTrue()
- assertThat(hierarchyOps[0].newParent).isEqualTo(secondDisplayArea.token.asBinder())
- assertThat(hierarchyOps[0].toTop).isTrue()
- }
- }
-
- @Test
- fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
- // Set up two display ids
- whenever(rootTaskDisplayAreaOrganizer.displayIds)
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
- // Create a mock for the target display area: default display
- val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .thenReturn(defaultDisplayArea)
-
- val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
- controller.moveToNextDisplay(task.taskId)
-
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- assertThat(hierarchyOps).hasSize(1)
- assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
- assertThat(hierarchyOps[0].isReparent).isTrue()
- assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder())
- assertThat(hierarchyOps[0].toTop).isTrue()
- }
- }
-
- @Test
- fun getTaskWindowingMode() {
- val fullscreenTask = setUpFullscreenTask()
- val freeformTask = setUpFreeformTask()
-
- assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId))
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(controller.getTaskWindowingMode(freeformTask.taskId))
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- fun onDesktopWindowClose_noActiveTasks() {
- val task = setUpFreeformTask(active = false)
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
- // Doesn't modify transaction
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
- val task = setUpFreeformTask()
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
- // Doesn't modify transaction
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
- // Adds remove wallpaper operation
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun onDesktopWindowClose_singleActiveTask_isClosing() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
- // Doesn't modify transaction
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun onDesktopWindowClose_singleActiveTask_isMinimized() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
- // Doesn't modify transaction
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun onDesktopWindowClose_multipleActiveTasks() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
- // Doesn't modify transaction
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
- // Adds remove wallpaper operation
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
- val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
- // Adds remove wallpaper operation
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
- val task = setUpFreeformTask(active = false)
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- controller.minimizeTask(task)
-
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- captor.value.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
- }
-
- @Test
- fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
- val task = setUpFreeformTask(active = true)
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
-
- controller.minimizeTask(task)
-
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- captor.value.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK
- }
- }
-
- @Test
- fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
- val task = setUpFreeformTask()
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- // The only active task is being minimized.
- controller.minimizeTask(task)
-
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- // Adds remove wallpaper operation
- captor.value.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntRemoveWallpaper() {
- val task = setUpFreeformTask()
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
-
- // The only active task is already minimized.
- controller.minimizeTask(task)
-
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- captor.value.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
- }
-
- @Test
- fun onDesktopWindowMinimize_multipleActiveTasks_doesntRemoveWallpaper() {
- val task1 = setUpFreeformTask(active = true)
- setUpFreeformTask(active = true)
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- controller.minimizeTask(task1)
-
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- captor.value.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
- }
-
- @Test
- fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
- val task1 = setUpFreeformTask(active = true)
- val task2 = setUpFreeformTask(active = true)
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- val wallpaperToken = MockToken().token()
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
- // task1 is the only visible task as task2 is minimized.
- controller.minimizeTask(task1)
- // Adds remove wallpaper operation
- val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
- // Adds remove wallpaper operation
- captor.value.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun onDesktopWindowMinimize_triesToExitImmersive() {
- val task = setUpFreeformTask()
- val transition = Binder()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
-
- controller.minimizeTask(task)
-
- verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
- }
-
- @Test
- fun onDesktopWindowMinimize_invokesImmersiveTransitionStartCallback() {
- val task = setUpFreeformTask()
- val transition = Binder()
- val runOnTransit = RunOnStartTransitionCallback()
- whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
- .thenReturn(transition)
- whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = task.taskId,
- runOnTransitionStart = runOnTransit,
- ))
-
- controller.minimizeTask(task)
-
- assertThat(runOnTransit.invocations).isEqualTo(1)
- assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
- }
-
- @Test
- fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
- val fullscreenTask = createFullscreenTask()
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- assertNotNull(wct, "should handle request")
- assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
-
- assertThat(wct.hierarchyOps).hasSize(1)
- }
-
- @Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
- val fullscreenTask = createFullscreenTask()
- fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- assertNotNull(wct, "should handle request")
- assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
-
- // There are 5 hops that are happening in this case:
- // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
- // 2. Bringing home task to front
- // 3. Pending intent for the wallpaper
- // 4. Bringing the existing freeform task to top
- // 5. Bringing the fullscreen task back at the top
- assertThat(wct.hierarchyOps).hasSize(5)
- wct.assertReorderAt(1, homeTask, toTop = true)
- wct.assertReorderAt(4, fullscreenTask, toTop = true)
- }
-
- @Test
- fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
- val fullscreenTask = createFullscreenTask()
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- // Make sure we only reorder the new task to top (we don't reorder the old task to bottom)
- assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
- wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
- }
-
- @Test
- fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- freeformTasks.forEach { markTaskVisible(it) }
- val fullscreenTask = createFullscreenTask()
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- // Make sure we reorder the new task to top, and the back task to the bottom
- assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
- wct.assertReorderAt(0, fullscreenTask, toTop = true)
- wct.assertReorderAt(1, freeformTasks[0], toTop = false)
- }
-
- @Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- freeformTasks.forEach { markTaskVisible(it) }
- val fullscreenTask = createFullscreenTask()
- fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- // Make sure we reorder the new task to top, and the back task to the bottom
- assertThat(wct!!.hierarchyOps.size).isEqualTo(9)
- wct.assertReorderAt(0, fullscreenTask, toTop = true)
- wct.assertReorderAt(8, freeformTasks[0], toTop = false)
- }
-
- @Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
- val minimizedTask = setUpFreeformTask()
- taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- freeformTasks.forEach { markTaskVisible(it) }
- val homeTask = setUpHomeTask()
- val fullscreenTask = createFullscreenTask()
- fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
- wct.assertReorderAt(0, fullscreenTask, toTop = true)
- // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
- // task is under the home task.
- wct.assertReorderAt(1, homeTask, toTop = true)
- wct.assertReorderAt(9, freeformTasks[0], toTop = false)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
- whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
- val fullscreenTask = createFullscreenTask()
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- assertNotNull(wct, "should handle request")
- assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(wct.hierarchyOps).hasSize(3)
- // There are 3 hops that are happening in this case:
- // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
- // 2. Pending intent for the wallpaper
- // 3. Bringing the fullscreen task back at the top
- wct.assertPendingIntentAt(1, desktopWallpaperIntent)
- wct.assertReorderAt(2, fullscreenTask, toTop = true)
- }
-
- @Test
- fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
- whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
- val fullscreenTask = createFullscreenTask()
- val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
- assertThat(wct).isNull()
- }
-
- @Test
- fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
- val freeformTask = setUpFreeformTask()
- markTaskHidden(freeformTask)
- val fullscreenTask = createFullscreenTask()
- assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
- }
-
- @Test
- fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
- val fullscreenTask = createFullscreenTask()
- assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
- }
-
- @Test
- fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
- val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
- createFreeformTask(displayId = SECOND_DISPLAY)
-
- val result = controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
- assertThat(result).isNull()
- }
-
- @Test
- fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- freeformTasks.forEach { markTaskVisible(it) }
- val newFreeformTask = createFreeformTask()
-
- val wct = controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
-
- assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
- wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
- }
-
- @Test
- fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
- val freeformTask = setUpFreeformTask()
- markTaskHidden(freeformTask)
-
- val wct =
- controller.handleRequest(Binder(), createTransition(freeformTask))
-
- // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
- assertNotNull(wct, "should handle request")
- assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() {
- whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
- val freeformTask = setUpFreeformTask()
- markTaskHidden(freeformTask)
- val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
- assertNotNull(wct, "should handle request")
- assertFalse(wct.anyWindowingModeChange(freeformTask.token))
- }
-
- @Test
- fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() {
- whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
- val freeformTask = setUpFreeformTask()
- markTaskHidden(freeformTask)
- val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
- assertNotNull(wct, "should handle request")
- assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
- val freeformTask1 = setUpFreeformTask()
- val freeformTask2 = createFreeformTask()
-
- markTaskHidden(freeformTask1)
- val result =
- controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(2)
- result.assertReorderAt(1, freeformTask2, toTop = true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
- val freeformTask1 = setUpFreeformTask()
- val freeformTask2 = createFreeformTask()
-
- markTaskHidden(freeformTask1)
- val result =
- controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(3)
- // Add desktop wallpaper activity
- result.assertPendingIntentAt(0, desktopWallpaperIntent)
- // Bring active desktop tasks to front
- result.assertReorderAt(1, freeformTask1, toTop = true)
- // Bring new task to front
- result.assertReorderAt(2, freeformTask2, toTop = true)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
- val task = createFreeformTask()
- val result = controller.handleRequest(Binder(), createTransition(task))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(1)
- result.assertReorderAt(0, task, toTop = true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
- val task = createFreeformTask()
- val result = controller.handleRequest(Binder(), createTransition(task))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(2)
- // Add desktop wallpaper activity
- result.assertPendingIntentAt(0, desktopWallpaperIntent)
- // Bring new task to front
- result.assertReorderAt(1, task, toTop = true)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
- val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
- // Second display task
- createFreeformTask(displayId = SECOND_DISPLAY)
-
- val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(1)
- result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
- val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
- // Second display task
- createFreeformTask(displayId = SECOND_DISPLAY)
-
- val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
-
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(2)
- // Add desktop wallpaper activity
- result.assertPendingIntentAt(0, desktopWallpaperIntent)
- // Bring new task to front
- result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
- }
-
- @Test
- fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
- whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false)
-
- val freeformTask1 = setUpFreeformTask()
- markTaskVisible(freeformTask1)
-
- val freeformTask2 = createFreeformTask()
- val result =
- controller.handleRequest(freeformTask2.token.asBinder(), createTransition(freeformTask2))
- assertFalse(result.anyDensityConfigChange(freeformTask2.token))
- }
-
- @Test
- fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
- whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true)
-
- val freeformTask1 = setUpFreeformTask()
- markTaskVisible(freeformTask1)
-
- val freeformTask2 = createFreeformTask()
- val result =
- controller.handleRequest(freeformTask2.token.asBinder(), createTransition(freeformTask2))
- assertTrue(result.anyDensityConfigChange(freeformTask2.token))
- }
-
- @Test
- fun handleRequest_freeformTask_keyguardLocked_returnNull() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
- val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
-
- val result = controller.handleRequest(Binder(), createTransition(freeformTask))
-
- assertNull(result, "Should NOT handle request")
- }
-
- @Test
- fun handleRequest_notOpenOrToFrontTransition_returnNull() {
- val task =
- TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .build()
- val transition = createTransition(task = task, type = TRANSIT_CLOSE)
- val result = controller.handleRequest(Binder(), transition)
- assertThat(result).isNull()
- }
-
- @Test
- fun handleRequest_noTriggerTask_returnNull() {
- assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
- }
-
- @Test
- fun handleRequest_triggerTaskNotStandard_returnNull() {
- val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
- assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
- }
-
- @Test
- fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
- val task =
- TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
- .build()
- assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
- }
-
- @Test
- fun handleRequest_recentsAnimationRunning_returnNull() {
- // Set up a visible freeform task so a fullscreen task should be converted to freeform
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- // Mark recents animation running
- recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
-
- // Open a fullscreen task, check that it does not result in a WCT with changes to it
- val fullscreenTask = createFullscreenTask()
- assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
- }
-
- @Test
- fun handleRequest_recentsAnimationRunning_relaunchActiveTask_taskBecomesUndefined() {
- // Set up a visible freeform task
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- // Mark recents animation running
- recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
-
- // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
- val result = controller.handleRequest(Binder(), createTransition(freeformTask))
- assertThat(result?.changes?.get(freeformTask.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- val task =
- setUpFullscreenTask().apply {
- isTopActivityTransparent = true
- isTopActivityNoDisplay = true
- numActivities = 1
- }
-
- val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ fun handleRequest_backTransition_multipleTasksSingleNonClosing_removesWallpaperAndTask() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_nonMinimizadTask_withWallpaper_removesWallpaper() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ // Task is being minimized so mark it as not visible.
+ taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleTaskNoToken_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleTaskWithToken_withWallpaper_removesWallpaper() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleTasksFlagEnabled_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ taskRepository.wallpaperActivityToken = MockToken().token()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleTasksSingleNonClosing_removesWallpaper() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleTasksSingleNonMinimized_removesWallpaper() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ // Should create remove wallpaper transaction
+ assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ // Task is being minimized so mark it as not visible.
+ taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
+ val result =
+ controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop() {
+ val task1 = setUpFullscreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+
+ controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- val task =
- setUpFreeformTask().apply {
- isTopActivityTransparent = true
- isTopActivityNoDisplay = false
- numActivities = 1
- }
-
- val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ }
+
+ @Test
+ fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop() {
+ val task1 = setUpSplitScreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+ val task4 = setUpSplitScreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+ task4.isFocused = true
+
+ task4.parentTaskId = task1.taskId
+
+ controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController)
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
+
+ @Test
+ fun moveFocusedTaskToFullscreen() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+
+ controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- // Set task as systemUI package
- val systemUIPackageName = context.resources.getString(
- com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- val task =
- setUpFreeformTask().apply {
- baseActivity = baseComponent
- isTopActivityNoDisplay = false
- }
-
- val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ }
+
+ @Test
+ fun moveFocusedTaskToFullscreen_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+ taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
+
+ controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
+ assertThat(taskChange.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
- }
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun moveFocusedTaskToFullscreen_multipleVisibleTasks_doesNotRemoveWallpaperActivity() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+ val wct = getLatestExitDesktopWct()
+ val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
+ assertThat(taskChange.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ // Does not remove wallpaper activity, as desktop still has visible desktop tasks
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun removeDesktop_multipleTasks_removesAll() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+ controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+
+ val wct = getLatestWct(TRANSIT_CLOSE)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ wct.assertRemoveAt(index = 0, task1.token)
+ wct.assertRemoveAt(index = 1, task2.token)
+ wct.assertRemoveAt(index = 2, task3.token)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun removeDesktop_multipleTasksWithBackgroundTask_removesAll() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task3.taskId)).thenReturn(null)
+
+ controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+
+ val wct = getLatestWct(TRANSIT_CLOSE)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ wct.assertRemoveAt(index = 0, task1.token)
+ wct.assertRemoveAt(index = 1, task2.token)
+ verify(recentTasksController).removeBackgroundTask(task3.taskId)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask()
+ setUpLandscapeDisplay()
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
- val freeformTask = setUpFreeformTask()
- markTaskVisible(freeformTask)
-
- // Set task as systemUI package
- val systemUIPackageName = context.resources.getString(
- com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- val task =
- setUpFullscreenTask().apply {
- baseActivity = baseComponent
- isTopActivityNoDisplay = true
- }
-
- val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
- val task = setUpFreeformTask()
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
- val task = setUpFreeformTask()
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
- )
- fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
- val task = setUpFreeformTask()
- markTaskHidden(task)
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_singleTaskNoToken_doesNotHandle() {
- val task = setUpFreeformTask()
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_backTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
- val task = setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_backTransition_multipleTasks_noWallpaper_doesNotHandle() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_multipleTasks_doesNotHandle() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
- )
- fun handleRequest_backTransition_multipleTasksSingleNonClosing_removesWallpaperAndTask() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
- )
- fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_backTransition_nonMinimizadTask_withWallpaper_removesWallpaper() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- // Task is being minimized so mark it as not visible.
- taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
- val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_closeTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
- val task = setUpFreeformTask()
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_singleTaskNoToken_doesNotHandle() {
- val task = setUpFreeformTask()
-
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
- val task = setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_singleTaskWithToken_withWallpaper_removesWallpaper() {
- val task = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_multipleTasksFlagEnabled_doesNotHandle() {
- val task1 = setUpFreeformTask()
- setUpFreeformTask()
-
- taskRepository.wallpaperActivityToken = MockToken().token()
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_multipleTasksSingleNonClosing_removesWallpaper() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_closeTransition_multipleTasksSingleNonMinimized_removesWallpaper() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
- // Should create remove wallpaper transaction
- assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
- fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() {
- val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val wallpaperToken = MockToken().token()
-
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
- // Task is being minimized so mark it as not visible.
- taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
- val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
-
- assertNull(result, "Should not handle request")
- }
-
- @Test
- fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop() {
- val task1 = setUpFullscreenTask()
- val task2 = setUpFullscreenTask()
- val task3 = setUpFullscreenTask()
-
- task1.isFocused = true
- task2.isFocused = false
- task3.isFocused = false
-
- controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop() {
- val task1 = setUpSplitScreenTask()
- val task2 = setUpFullscreenTask()
- val task3 = setUpFullscreenTask()
- val task4 = setUpSplitScreenTask()
-
- task1.isFocused = true
- task2.isFocused = false
- task3.isFocused = false
- task4.isFocused = true
-
- task4.parentTaskId = task1.taskId
-
- controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(splitScreenController)
- .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
- }
-
- @Test
- fun moveFocusedTaskToFullscreen() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
-
- controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
- }
-
- @Test
- fun moveFocusedTaskToFullscreen_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
- taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
-
- controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
- assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
- wct.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun moveFocusedTaskToFullscreen_multipleVisibleTasks_doesNotRemoveWallpaperActivity() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
- taskRepository.wallpaperActivityToken = wallpaperToken
- controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
- val wct = getLatestExitDesktopWct()
- val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
- assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
- // Does not remove wallpaper activity, as desktop still has visible desktop tasks
- assertThat(wct.hierarchyOps).isEmpty()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun removeDesktop_multipleTasks_removesAll() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
- controller.removeDesktop(displayId = DEFAULT_DISPLAY)
-
- val wct = getLatestWct(TRANSIT_CLOSE)
- assertThat(wct.hierarchyOps).hasSize(3)
- wct.assertRemoveAt(index = 0, task1.token)
- wct.assertRemoveAt(index = 1, task2.token)
- wct.assertRemoveAt(index = 2, task3.token)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun removeDesktop_multipleTasksWithBackgroundTask_removesAll() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
- whenever(shellTaskOrganizer.getRunningTaskInfo(task3.taskId)).thenReturn(null)
-
- controller.removeDesktop(displayId = DEFAULT_DISPLAY)
-
- val wct = getLatestWct(TRANSIT_CLOSE)
- assertThat(wct.hierarchyOps).hasSize(2)
- wct.assertRemoveAt(index = 0, task1.token)
- wct.assertRemoveAt(index = 1, task2.token)
- verify(recentTasksController).removeBackgroundTask(task3.taskId)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task = setUpFullscreenTask()
- setUpLandscapeDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
- setUpLandscapeDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
- setUpLandscapeDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
- setUpLandscapeDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(
- isResizable = false,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
- shouldLetterbox = true)
- setUpLandscapeDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
- shouldLetterbox = true)
- setUpPortraitDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(
- isResizable = false,
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
- val spyController = spy(controller)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
- val task =
- setUpFullscreenTask(
- isResizable = false,
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
- shouldLetterbox = true)
- setUpPortraitDisplay()
-
- spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
- val wct = getLatestDragToDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
- }
-
- @Test
- fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
- val task = setUpFreeformTask()
- val spyController = spy(controller)
- val mockSurface = mock(SurfaceControl::class.java)
- val mockDisplayLayout = mock(DisplayLayout::class.java)
- whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
-
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
- spyController.onDragPositioningEnd(
- task,
- mockSurface,
- Point(100, -100), /* position */
- PointF(200f, -200f), /* inputCoordinate */
- Rect(100, -100, 500, 1000), /* currentDragBounds */
- Rect(0, 50, 2000, 2000), /* validDragArea */
- Rect() /* dragStartBounds */,
- motionEvent,
- desktopWindowDecoration,
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ shouldLetterbox = true,
+ )
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+ )
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ shouldLetterbox = true,
+ )
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ )
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+ shouldLetterbox = true,
+ )
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ )
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+ shouldLetterbox = true,
+ )
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
+ val task = setUpFreeformTask()
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
+
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ Point(100, -100), /* position */
+ PointF(200f, -200f), /* inputCoordinate */
+ Rect(100, -100, 500, 1000), /* currentDragBounds */
+ Rect(0, 50, 2000, 2000), /* validDragArea */
+ Rect() /* dragStartBounds */,
+ motionEvent,
+ desktopWindowDecoration,
)
- val rectAfterEnd = Rect(100, 50, 500, 1150)
- verify(transitions)
- .startTransition(
- eq(TRANSIT_CHANGE),
- Mockito.argThat { wct ->
- return@argThat wct.changes.any { (token, change) ->
- change.configuration.windowConfiguration.bounds == rectAfterEnd
- }
- },
- eq(null))
- }
-
- @Test
- fun onDesktopDragEnd_noIndicator_updatesTaskBounds() {
- val task = setUpFreeformTask()
- val spyController = spy(controller)
- val mockSurface = mock(SurfaceControl::class.java)
- val mockDisplayLayout = mock(DisplayLayout::class.java)
- whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
-
- val currentDragBounds = Rect(100, 200, 500, 1000)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
-
- spyController.onDragPositioningEnd(
- task,
- mockSurface,
- Point(100, 200), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- currentDragBounds, /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
- motionEvent,
- desktopWindowDecoration,
- )
-
-
- verify(transitions)
- .startTransition(
- eq(TRANSIT_CHANGE),
- Mockito.argThat { wct ->
- return@argThat wct.changes.any { (token, change) ->
- change.configuration.windowConfiguration.bounds == currentDragBounds
- }
- },
- eq(null))
- }
-
- @Test
- fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() {
- val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
- val spyController = spy(controller)
- val mockSurface = mock(SurfaceControl::class.java)
- val mockDisplayLayout = mock(DisplayLayout::class.java)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
- }
- whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(false)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
-
- // Drag move the task to the top edge
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
- spyController.onDragPositioningEnd(
- task,
- mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- Rect(100, 50, 500, 1000), /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
- motionEvent,
- desktopWindowDecoration)
-
- // Assert the task exits desktop mode
- val wct = getLatestExitDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
-
- @Test
- fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() {
- val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
- val spyController = spy(controller)
- val mockSurface = mock(SurfaceControl::class.java)
- val mockDisplayLayout = mock(DisplayLayout::class.java)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
- }
- whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
-
- // Drag move the task to the top edge
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
- spyController.onDragPositioningEnd(
- task,
- mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- Rect(100, 50, 500, 1000), /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
- motionEvent,
- desktopWindowDecoration)
-
- // Assert bounds set to stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
- // Assert event is properly logged
- verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
- ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
- InputMethod.UNKNOWN_INPUT_METHOD,
- task,
- task.configuration.windowConfiguration.bounds.width(),
- task.configuration.windowConfiguration.bounds.height(),
- displayController
- )
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
- InputMethod.UNKNOWN_INPUT_METHOD,
- task,
- STABLE_BOUNDS.width(),
- STABLE_BOUNDS.height(),
- displayController
- )
- }
-
- @Test
- fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_noBoundsChange() {
- val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
- val spyController = spy(controller)
- val mockSurface = mock(SurfaceControl::class.java)
- val mockDisplayLayout = mock(DisplayLayout::class.java)
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
- }
- whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
- .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
-
- // Drag move the task to the top edge
- val currentDragBounds = Rect(100, 50, 500, 1000)
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
- spyController.onDragPositioningEnd(
- task,
- mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- currentDragBounds, /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
- motionEvent,
- desktopWindowDecoration)
-
- // Assert that task is NOT updated via WCT
- verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
- // Assert that task leash is updated via Surface Animations
- verify(mReturnToDragStartAnimator).start(
- eq(task.taskId),
- eq(mockSurface),
- eq(currentDragBounds),
- eq(STABLE_BOUNDS),
- anyOrNull(),
- )
- // Assert no event is logged
- verify(desktopModeEventLogger, never()).logTaskResizingStarted(
- any(), any(), any(), any(), any(), any(), any()
- )
- verify(desktopModeEventLogger, never()).logTaskResizingEnded(
- any(), any(), any(), any(), any(), any(), any()
- )
- }
-
- @Test
- fun enterSplit_freeformTaskIsMovedToSplit() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
-
- controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
- verify(splitScreenController)
- .requestEnterSplitSelect(
- eq(task2),
- any(),
- eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
- eq(task2.configuration.windowConfiguration.bounds))
- }
-
- @Test
- fun enterSplit_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
- taskRepository.wallpaperActivityToken = wallpaperToken
- taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
- taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
-
- controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
- val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(splitScreenController)
- .requestEnterSplitSelect(
- eq(task2),
- wctArgument.capture(),
- eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
- eq(task2.configuration.windowConfiguration.bounds))
- // Removes wallpaper activity when leaving desktop
- wctArgument.value.assertRemoveAt(index = 0, wallpaperToken)
- }
-
- @Test
- fun enterSplit_multipleVisibleNonMinimizedTasks_removesWallpaperActivity() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- val task3 = setUpFreeformTask()
- val wallpaperToken = MockToken().token()
-
- task1.isFocused = false
- task2.isFocused = true
- task3.isFocused = false
- taskRepository.wallpaperActivityToken = wallpaperToken
-
- controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
- val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(splitScreenController)
- .requestEnterSplitSelect(
- eq(task2),
- wctArgument.capture(),
- eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
- eq(task2.configuration.windowConfiguration.bounds))
- // Does not remove wallpaper activity, as desktop still has visible desktop tasks
- assertThat(wctArgument.value.hierarchyOps).isEmpty()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun newWindow_fromFullscreenOpensInSplit() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask()
- val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
- runOpenNewWindow(task)
- verify(splitScreenController)
- .startIntent(any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
- )
- assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
- .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun newWindow_fromSplitOpensInSplit() {
- setUpLandscapeDisplay()
- val task = setUpSplitScreenTask()
- val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
- runOpenNewWindow(task)
- verify(splitScreenController)
- .startIntent(
- any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
- )
- assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
- .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun newWindow_fromFreeformAddsNewWindow() {
- setUpLandscapeDisplay()
- val task = setUpFreeformTask()
- val wctCaptor = argumentCaptor<WindowContainerTransaction>()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
- .thenReturn(ExitResult.NoExit)
- whenever(desktopMixedTransitionHandler
- .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
- .thenReturn(transition)
-
- runOpenNewWindow(task)
-
- verify(desktopMixedTransitionHandler)
- .startLaunchTransition(anyInt(), wctCaptor.capture(), anyOrNull(), anyOrNull(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
- .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
- setUpLandscapeDisplay()
- val immersiveTask = setUpFreeformTask()
- val task = setUpFreeformTask()
- val runOnStart = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
- .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
- whenever(desktopMixedTransitionHandler
- .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
- .thenReturn(transition)
-
- runOpenNewWindow(task)
-
- runOnStart.assertOnlyInvocation(transition)
- }
-
- private fun runOpenNewWindow(task: RunningTaskInfo) {
- markTaskVisible(task)
- task.baseActivity = mock(ComponentName::class.java)
- task.isFocused = true
- runningTasks.add(task)
- controller.openNewWindow(task)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFullscreenOpensInSplit() {
- setUpLandscapeDisplay()
- val task = setUpFullscreenTask()
- val taskToRequest = setUpFreeformTask()
- val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
- runOpenInstance(task, taskToRequest.taskId)
- verify(splitScreenController)
- .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
- .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromSplitOpensInSplit() {
- setUpLandscapeDisplay()
- val task = setUpSplitScreenTask()
- val taskToRequest = setUpFreeformTask()
- val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
- runOpenInstance(task, taskToRequest.taskId)
- verify(splitScreenController)
- .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
- .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFreeformAddsNewWindow() {
- setUpLandscapeDisplay()
- val task = setUpFreeformTask()
- val taskToRequest = setUpFreeformTask()
- val wctCaptor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- runOpenInstance(task, taskToRequest.taskId)
- verify(transitions).startTransition(anyInt(), wctCaptor.capture(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(wctCaptor.value.hierarchyOps[0].launchOptions)
- .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFreeform_minimizesIfNeeded() {
- setUpLandscapeDisplay()
- val homeTask = setUpHomeTask()
- val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
- val oldestTask = freeformTasks.first()
- val newestTask = freeformTasks.last()
-
- runOpenInstance(newestTask, freeformTasks[1].taskId)
-
- val wct = getLatestWct(type = TRANSIT_OPEN)
- // Home is moved to front of everything.
- assertThat(
- wct.hierarchyOps.any { hop ->
- hop.container == homeTask.token.asBinder() && hop.toTop
- }
- ).isTrue()
- // And the oldest task isn't moved in front of home, effectively minimizing it.
- assertThat(
- wct.hierarchyOps.none { hop ->
- hop.container == oldestTask.token.asBinder() && hop.toTop
- }
- ).isTrue()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
- setUpLandscapeDisplay()
- val homeTask = setUpHomeTask()
- val freeformTask = setUpFreeformTask()
- val immersiveTask = setUpFreeformTask()
- taskRepository.setTaskInFullImmersiveState(
- displayId = immersiveTask.displayId,
- taskId = immersiveTask.taskId,
- immersive = true
- )
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
- .thenReturn(transition)
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(
- any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = immersiveTask.taskId,
- runOnTransitionStart = runOnStartTransit,
- ))
-
- runOpenInstance(immersiveTask, freeformTask.taskId)
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
-
- private fun runOpenInstance(
- callingTask: RunningTaskInfo,
- requestedTaskId: Int
- ) {
- markTaskVisible(callingTask)
- callingTask.baseActivity = mock(ComponentName::class.java)
- callingTask.isFocused = true
- runningTasks.add(callingTask)
- controller.openInstance(callingTask, requestedTaskId)
- }
-
- @Test
- fun toggleBounds_togglesToStableBounds() {
- val bounds = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
-
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
+ val rectAfterEnd = Rect(100, 50, 500, 1150)
+ verify(transitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ Mockito.argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ },
+ eq(null),
+ )
+ }
- // Assert bounds set to stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- STABLE_BOUNDS.width(),
- STABLE_BOUNDS.height(),
- displayController
- )
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
- fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
- val bounds = Rect(100, 100, 300, 300)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
- topActivityInfo = ActivityInfo().apply {
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
- configuration.windowConfiguration.appBounds = bounds
- }
- isResizeable = true
- }
-
- val currentDragBounds = Rect(0, 100, 200, 300)
- val expectedBounds = Rect(
- STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
- )
+ @Test
+ fun onDesktopDragEnd_noIndicator_updatesTaskBounds() {
+ val task = setUpFreeformTask()
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+
+ val currentDragBounds = Rect(100, 200, 500, 1000)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ Point(100, 200), /* position */
+ PointF(200f, 300f), /* inputCoordinate */
+ currentDragBounds, /* currentDragBounds */
+ Rect(0, 50, 2000, 2000) /* validDragArea */,
+ Rect() /* dragStartBounds */,
+ motionEvent,
+ desktopWindowDecoration,
+ )
- controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
- ResizeTrigger.SNAP_LEFT_MENU, InputMethod.TOUCH, desktopWindowDecoration)
- // Assert bounds set to stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
- assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.SNAP_LEFT_MENU,
- InputMethod.TOUCH,
- task,
- expectedBounds.width(),
- expectedBounds.height(),
- displayController
- )
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
- fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
- // Set up task to already be in snapped-left bounds
- val bounds = Rect(
- STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
- )
- val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
- topActivityInfo = ActivityInfo().apply {
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
- configuration.windowConfiguration.appBounds = bounds
- }
- isResizeable = true
- }
-
- // Attempt to snap left again
- val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
- controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
- ResizeTrigger.SNAP_LEFT_MENU, InputMethod.TOUCH, desktopWindowDecoration)
- // Assert that task is NOT updated via WCT
- verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
-
- // Assert that task leash is updated via Surface Animations
- verify(mReturnToDragStartAnimator).start(
- eq(task.taskId),
- eq(mockSurface),
- eq(currentDragBounds),
- eq(bounds),
- anyOrNull(),
- )
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.SNAP_LEFT_MENU,
- InputMethod.TOUCH,
- task,
- bounds.width(),
- bounds.height(),
- displayController
- )
- }
+ verify(transitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ Mockito.argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ change.configuration.windowConfiguration.bounds == currentDragBounds
+ }
+ },
+ eq(null),
+ )
+ }
+
+ @Test
+ fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() {
+ val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(false)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+ // Drag move the task to the top edge
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ Point(100, 50), /* position */
+ PointF(200f, 300f), /* inputCoordinate */
+ Rect(100, 50, 500, 1000), /* currentDragBounds */
+ Rect(0, 50, 2000, 2000) /* validDragArea */,
+ Rect() /* dragStartBounds */,
+ motionEvent,
+ desktopWindowDecoration,
+ )
- @Test
- @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING, Flags.FLAG_ENABLE_TILE_RESIZING)
- fun handleSnapResizingTaskOnDrag_nonResizable_snapsToHalfScreen() {
- val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
- isResizeable = false
+ // Assert the task exits desktop mode
+ val wct = getLatestExitDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
}
- val preDragBounds = Rect(100, 100, 400, 500)
- val currentDragBounds = Rect(0, 100, 300, 500)
- val expectedBounds =
- Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)
- controller.handleSnapResizingTaskOnDrag(
+ @Test
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() {
+ val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+ // Drag move the task to the top edge
+ val currentDragBounds = Rect(100, 50, 500, 1000)
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ Point(100, 50), /* position */
+ PointF(200f, 300f), /* inputCoordinate */
+ currentDragBounds,
+ Rect(0, 50, 2000, 2000) /* validDragArea */,
+ Rect() /* dragStartBounds */,
+ motionEvent,
+ desktopWindowDecoration,
+ )
- task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
- desktopWindowDecoration
- )
- val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
- assertThat(findBoundsChange(wct, task)).isEqualTo(
- expectedBounds
- )
- verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
- ResizeTrigger.DRAG_LEFT,
- InputMethod.UNKNOWN_INPUT_METHOD,
- task,
- preDragBounds.width(),
- preDragBounds.height(),
- displayController
- )
- }
-
- @Test
- @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
- fun handleSnapResizingTaskOnDrag_nonResizable_startsRepositionAnimation() {
- val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
- isResizeable = false
- }
- val preDragBounds = Rect(100, 100, 400, 500)
- val currentDragBounds = Rect(0, 100, 300, 500)
-
- controller.handleSnapResizingTaskOnDrag(
- task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
- desktopWindowDecoration)
- verify(mReturnToDragStartAnimator).start(
- eq(task.taskId),
- eq(mockSurface),
- eq(currentDragBounds),
- eq(preDragBounds),
- any(),
- )
- verify(desktopModeEventLogger, never()).logTaskResizingStarted(
- any(),
- any(),
- any(),
- any(),
- any(),
- any(),
- any()
- )
- }
-
- @Test
- @EnableFlags(
- Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING
- )
- fun handleInstantSnapResizingTask_nonResizable_animatorNotStartedAndShowsToast() {
- val taskBounds = Rect(0, 0, 200, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
- isResizeable = false
- }
-
- controller.handleInstantSnapResizingTask(
- task,
- SnapPosition.LEFT,
- ResizeTrigger.SNAP_LEFT_MENU,
- InputMethod.MOUSE,
- desktopWindowDecoration
- )
+ // Assert bounds set to stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+ // Assert event is properly logged
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingStarted(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ task,
+ task.configuration.windowConfiguration.bounds.width(),
+ task.configuration.windowConfiguration.bounds.height(),
+ displayController,
+ )
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ task,
+ STABLE_BOUNDS.width(),
+ STABLE_BOUNDS.height(),
+ displayController,
+ )
+ }
- // Assert that task is NOT updated via WCT
- verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
- verify(mockToast).show()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
- @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
- fun handleInstantSnapResizingTask_resizable_snapsToHalfScreenAndNotShowToast() {
- val taskBounds = Rect(0, 0, 200, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
- isResizeable = true
- }
- val expectedBounds = Rect(
- STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
- )
+ @Test
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_noBoundsChange() {
+ val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+ // Drag move the task to the top edge
+ val currentDragBounds = Rect(100, 50, 500, 1000)
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ Point(100, 50), /* position */
+ PointF(200f, 300f), /* inputCoordinate */
+ currentDragBounds, /* currentDragBounds */
+ Rect(0, 50, 2000, 2000) /* validDragArea */,
+ Rect() /* dragStartBounds */,
+ motionEvent,
+ desktopWindowDecoration,
+ )
- controller.handleInstantSnapResizingTask(
- task, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, InputMethod.MOUSE,
- desktopWindowDecoration
- )
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator)
+ .start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(STABLE_BOUNDS),
+ anyOrNull(),
+ )
+ // Assert no event is logged
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingStarted(any(), any(), any(), any(), any(), any(), any())
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingEnded(any(), any(), any(), any(), any(), any(), any())
+ }
- // Assert bounds set to half of the stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct(taskBounds)
- assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
- verify(mockToast, never()).show()
- verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
- ResizeTrigger.SNAP_LEFT_MENU,
- InputMethod.MOUSE,
- task,
- taskBounds.width(),
- taskBounds.height(),
- displayController
- )
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.SNAP_LEFT_MENU,
- InputMethod.MOUSE,
- task,
- expectedBounds.width(),
- expectedBounds.height(),
- displayController
- )
- }
-
- @Test
- fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
- val bounds = Rect(0, 0, 200, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
- topActivityInfo = ActivityInfo().apply {
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
- configuration.windowConfiguration.appBounds = bounds
- }
- appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
- appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
- isResizeable = false
- }
-
- // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
- val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
-
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ fun enterSplit_freeformTaskIsMovedToSplit() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+
+ controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+ verify(splitScreenController)
+ .requestEnterSplitSelect(
+ eq(task2),
+ any(),
+ eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+ eq(task2.configuration.windowConfiguration.bounds),
+ )
+ }
- // Assert bounds set to stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- expectedBounds.width(),
- expectedBounds.height(),
- displayController
- )
- }
-
- @Test
- fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
- val bounds = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
-
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ fun enterSplit_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+ taskRepository.wallpaperActivityToken = wallpaperToken
+ taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+ taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
+
+ controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+ val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(splitScreenController)
+ .requestEnterSplitSelect(
+ eq(task2),
+ wctArgument.capture(),
+ eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+ eq(task2.configuration.windowConfiguration.bounds),
+ )
+ // Removes wallpaper activity when leaving desktop
+ wctArgument.value.assertRemoveAt(index = 0, wallpaperToken)
+ }
- assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
- verify(desktopModeEventLogger, never()).logTaskResizingEnded(
- any(), any(), any(), any(),
- any(), any(), any()
- )
- }
-
- @Test
- fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() {
- val boundsBeforeMaximize = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
-
- // Maximize
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
- task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
-
- // Restore
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.RESTORE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ fun enterSplit_multipleVisibleNonMinimizedTasks_removesWallpaperActivity() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val task3 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ task1.isFocused = false
+ task2.isFocused = true
+ task3.isFocused = false
+ taskRepository.wallpaperActivityToken = wallpaperToken
+
+ controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+ val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(splitScreenController)
+ .requestEnterSplitSelect(
+ eq(task2),
+ wctArgument.capture(),
+ eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+ eq(task2.configuration.windowConfiguration.bounds),
+ )
+ // Does not remove wallpaper activity, as desktop still has visible desktop tasks
+ assertThat(wctArgument.value.hierarchyOps).isEmpty()
+ }
- // Assert bounds set to last bounds before maximize
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- boundsBeforeMaximize.width(),
- boundsBeforeMaximize.height(),
- displayController
- )
- }
-
- @Test
- fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
- val boundsBeforeMaximize = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
- isResizeable = false
- }
-
- // Maximize
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
- task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left,
- boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom)
-
- // Restore
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.RESTORE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun newWindow_fromFullscreenOpensInSplit() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask()
+ val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+ runOpenNewWindow(task)
+ verify(splitScreenController)
+ .startIntent(
+ any(),
+ anyInt(),
+ any(),
+ any(),
+ optionsCaptor.capture(),
+ anyOrNull(),
+ eq(true),
+ eq(SPLIT_INDEX_UNDEFINED),
+ )
+ assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+ .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+ }
- // Assert bounds set to last bounds before maximize
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- boundsBeforeMaximize.width(),
- boundsBeforeMaximize.height(),
- displayController
- )
- }
-
- @Test
- fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
- val boundsBeforeMaximize = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
- isResizeable = false
- }
-
- // Maximize
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
- task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left,
- STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom)
-
- // Restore
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.RESTORE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun newWindow_fromSplitOpensInSplit() {
+ setUpLandscapeDisplay()
+ val task = setUpSplitScreenTask()
+ val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+ runOpenNewWindow(task)
+ verify(splitScreenController)
+ .startIntent(
+ any(),
+ anyInt(),
+ any(),
+ any(),
+ optionsCaptor.capture(),
+ anyOrNull(),
+ eq(true),
+ eq(SPLIT_INDEX_UNDEFINED),
+ )
+ assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+ .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+ }
- // Assert bounds set to last bounds before maximize
- val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- boundsBeforeMaximize.width(),
- boundsBeforeMaximize.height(),
- displayController
- )
- }
-
- @Test
- fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
- val boundsBeforeMaximize = Rect(0, 0, 100, 100)
- val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
-
- // Maximize
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.MAXIMIZE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
- InputMethod.TOUCH
- )
- )
- task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
-
- // Restore
- controller.toggleDesktopTaskSize(
- task,
- ToggleTaskSizeInteraction(
- ToggleTaskSizeInteraction.Direction.RESTORE,
- ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
- InputMethod.TOUCH
- )
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun newWindow_fromFreeformAddsNewWindow() {
+ setUpLandscapeDisplay()
+ val task = setUpFreeformTask()
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ anyInt(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.NoExit)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ runOpenNewWindow(task)
+
+ verify(desktopMixedTransitionHandler)
+ .startLaunchTransition(
+ anyInt(),
+ wctCaptor.capture(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ assertThat(
+ ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
+ .launchWindowingMode
+ )
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
- // Assert last bounds before maximize removed after use
- assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
- verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task,
- boundsBeforeMaximize.width(),
- boundsBeforeMaximize.height(),
- displayController
- )
- }
-
- @Test
- fun onUnhandledDrag_newFreeformIntent() {
- testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
- PointF(1200f, 700f),
- Rect(240, 700, 2160, 1900))
- }
-
- @Test
- fun onUnhandledDrag_newFreeformIntentSplitLeft() {
- testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
- PointF(50f, 700f),
- Rect(0, 0, 500, 1000))
- }
-
- @Test
- fun onUnhandledDrag_newFreeformIntentSplitRight() {
- testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
- PointF(2500f, 700f),
- Rect(500, 0, 1000, 1000))
- }
-
- @Test
- fun onUnhandledDrag_newFullscreenIntent() {
- testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
- PointF(1200f, 50f),
- Rect())
- }
-
- @Test
- fun shellController_registersUserChangeListener() {
- verify(shellController, times(1)).addUserChangeListener(any())
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
- val task = setUpFreeformTask(DEFAULT_DISPLAY)
- taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
-
- task.requestedVisibleTypes = WindowInsets.Type.statusBars()
- controller.onTaskInfoChanged(task)
-
- verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
- val task = setUpFreeformTask(DEFAULT_DISPLAY)
- taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
-
- task.requestedVisibleTypes = WindowInsets.Type.statusBars()
- controller.onTaskInfoChanged(task)
-
- verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_inRecentsTransition_noExit() {
- val task = setUpFreeformTask(DEFAULT_DISPLAY)
- taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
- recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED)
-
- task.requestedVisibleTypes = WindowInsets.Type.statusBars()
- controller.onTaskInfoChanged(task)
-
- verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
- }
-
- @Test
- fun moveTaskToDesktop_background_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = true)
- val wct = WindowContainerTransaction()
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = 5,
- runOnTransitionStart = runOnStartTransit,
- ))
- whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
-
- controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
-
- @Test
- fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = false)
- val wct = WindowContainerTransaction()
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = 5,
- runOnTransitionStart = runOnStartTransit,
- ))
- whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
-
- controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
-
- @Test
- fun moveTaskToFront_background_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = true)
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = 5,
- runOnTransitionStart = runOnStartTransit,
- ))
- whenever(desktopMixedTransitionHandler
- .startLaunchTransition(any(), any(), anyInt(), anyOrNull(), anyOrNull()))
- .thenReturn(transition)
-
- controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
-
- @Test
- fun moveTaskToFront_foreground_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = false)
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
- .thenReturn(
- ExitResult.Exit(
- exitingTask = 5,
- runOnTransitionStart = runOnStartTransit,
- ))
- whenever(desktopMixedTransitionHandler
- .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull(), anyOrNull()))
- .thenReturn(transition)
-
- controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
-
- @Test
- fun handleRequest_freeformLaunchToDesktop_attemptsImmersiveExit() {
- markTaskVisible(setUpFreeformTask())
- val task = setUpFreeformTask()
- markTaskVisible(task)
- val binder = Binder()
-
- controller.handleRequest(binder, createTransition(task))
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
- }
-
- @Test
- fun handleRequest_fullscreenLaunchToDesktop_attemptsImmersiveExit() {
- setUpFreeformTask()
- val task = setUpFullscreenTask()
- val binder = Binder()
-
- controller.handleRequest(binder, createTransition(task))
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
- val triggerTask = setUpFullscreenTask(displayId = 5)
- taskRepository.setTaskInFullImmersiveState(
- displayId = triggerTask.displayId,
- taskId = triggerTask.taskId,
- immersive = true
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
+ setUpLandscapeDisplay()
+ val immersiveTask = setUpFreeformTask()
+ val task = setUpFreeformTask()
+ val runOnStart = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ anyInt(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ runOpenNewWindow(task)
+
+ runOnStart.assertOnlyInvocation(transition)
+ }
- assertThat(controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )).isFalse()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5)
- taskRepository.setTaskInFullImmersiveState(
- displayId = triggerTask.displayId,
- taskId = triggerTask.taskId,
- immersive = true
- )
+ private fun runOpenNewWindow(task: RunningTaskInfo) {
+ markTaskVisible(task)
+ task.baseActivity = mock(ComponentName::class.java)
+ task.isFocused = true
+ runningTasks.add(task)
+ controller.openNewWindow(task)
+ }
- assertThat(controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null)
- )).isFalse()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5)
- taskRepository.setTaskInFullImmersiveState(
- displayId = triggerTask.displayId,
- taskId = triggerTask.taskId,
- immersive = false
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFullscreenOpensInSplit() {
+ setUpLandscapeDisplay()
+ val task = setUpFullscreenTask()
+ val taskToRequest = setUpFreeformTask()
+ val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+ runOpenInstance(task, taskToRequest.taskId)
+ verify(splitScreenController)
+ .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
+ assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+ .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+ }
- assertThat(controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )).isFalse()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() {
- // At least one freeform task to be in a desktop.
- val existingTask = setUpFreeformTask(displayId = 5)
- val triggerTask = setUpFullscreenTask(displayId = 5)
- assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
- taskRepository.setTaskInFullImmersiveState(
- displayId = existingTask.displayId,
- taskId = existingTask.taskId,
- immersive = true
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromSplitOpensInSplit() {
+ setUpLandscapeDisplay()
+ val task = setUpSplitScreenTask()
+ val taskToRequest = setUpFreeformTask()
+ val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+ runOpenInstance(task, taskToRequest.taskId)
+ verify(splitScreenController)
+ .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
+ assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+ .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+ }
- assertThat(
- controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )
- ).isTrue()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
- val triggerTask = setUpFullscreenTask(displayId = 5)
- assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
-
- assertThat(controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )).isFalse()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() {
- // At least one freeform task to be in a desktop.
- val existingTask = setUpFreeformTask(displayId = 5)
- val triggerTask = setUpFreeformTask(displayId = 5, active = false)
- assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
- taskRepository.setTaskInFullImmersiveState(
- displayId = existingTask.displayId,
- taskId = existingTask.taskId,
- immersive = true
- )
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFreeformAddsNewWindow() {
+ setUpLandscapeDisplay()
+ val task = setUpFreeformTask()
+ val taskToRequest = setUpFreeformTask()
+ runOpenInstance(task, taskToRequest.taskId)
+ verify(desktopMixedTransitionHandler)
+ .startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, taskToRequest)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFreeform_minimizesIfNeeded() {
+ setUpLandscapeDisplay()
+ val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+ val oldestTask = freeformTasks.first()
+ val newestTask = freeformTasks.last()
+
+ val transition = Binder()
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ wctCaptor.capture(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+ val wct = wctCaptor.firstValue
+ assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
+ wct.assertReorderAt(0, freeformTasks[1], toTop = true)
+ wct.assertReorderAt(1, oldestTask, toTop = false)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
+ setUpLandscapeDisplay()
+ val freeformTask = setUpFreeformTask()
+ val immersiveTask = setUpFreeformTask()
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = immersiveTask.displayId,
+ taskId = immersiveTask.taskId,
+ immersive = true,
+ )
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ any(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ eq(DEFAULT_DISPLAY),
+ eq(freeformTask.taskId),
+ any(),
+ )
+ )
+ .thenReturn(
+ ExitResult.Exit(
+ exitingTask = immersiveTask.taskId,
+ runOnTransitionStart = runOnStartTransit,
+ )
+ )
+
+ runOpenInstance(immersiveTask, freeformTask.taskId)
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(
+ any(),
+ eq(immersiveTask.displayId),
+ eq(freeformTask.taskId),
+ any(),
+ )
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
+ private fun runOpenInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
+ markTaskVisible(callingTask)
+ callingTask.baseActivity = mock(ComponentName::class.java)
+ callingTask.isFocused = true
+ runningTasks.add(callingTask)
+ controller.openInstance(callingTask, requestedTaskId)
+ }
+
+ @Test
+ fun toggleBounds_togglesToStableBounds() {
+ val bounds = Rect(0, 0, 100, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
+
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert bounds set to stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ STABLE_BOUNDS.width(),
+ STABLE_BOUNDS.height(),
+ displayController,
+ )
+ }
- assertThat(
- controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )
- ).isTrue()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5, active = false)
- assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
-
- assertThat(controller.shouldPlayDesktopAnimation(
- TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
- )).isFalse()
- }
-
- private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
- var invocations = 0
- private set
- var lastInvoked: IBinder? = null
- private set
-
- override fun invoke(transition: IBinder) {
- invocations++
- lastInvoked = transition
- }
- }
-
- private fun RunOnStartTransitionCallback.assertOnlyInvocation(transition: IBinder) {
- assertThat(invocations).isEqualTo(1)
- assertThat(lastInvoked).isEqualTo(transition)
- }
-
- /**
- * Assert that an unhandled drag event launches a PendingIntent with the
- * windowing mode and bounds we are expecting.
- */
- private fun testOnUnhandledDrag(
- indicatorType: DesktopModeVisualIndicator.IndicatorType,
- inputCoordinate: PointF,
- expectedBounds: Rect
- ) {
- setUpLandscapeDisplay()
- val task = setUpFreeformTask()
- markTaskVisible(task)
- task.isFocused = true
- val runningTasks = ArrayList<RunningTaskInfo>()
- runningTasks.add(task)
- val spyController = spy(controller)
- val mockPendingIntent = mock(PendingIntent::class.java)
- val mockDragEvent = mock(DragEvent::class.java)
- val mockCallback = mock(Consumer::class.java)
- val b = SurfaceControl.Builder()
- b.setName("test surface")
- val dragSurface = b.build()
- whenever(shellTaskOrganizer.runningTasks).thenReturn(runningTasks)
- whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
- whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
- whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
- whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
- whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
- doReturn(indicatorType)
- .whenever(spyController).updateVisualIndicator(
- eq(task),
- anyOrNull(),
- anyOrNull(),
- anyOrNull(),
- eq(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
- )
-
- spyController.onUnhandledDrag(
- mockPendingIntent,
- mockDragEvent,
- mockCallback as Consumer<Boolean>
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+ fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
+ val bounds = Rect(100, 100, 300, 300)
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo =
+ ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ isResizeable = true
+ }
+
+ val currentDragBounds = Rect(0, 100, 200, 300)
+ val expectedBounds =
+ Rect(
+ STABLE_BOUNDS.left,
+ STABLE_BOUNDS.top,
+ STABLE_BOUNDS.right / 2,
+ STABLE_BOUNDS.bottom,
+ )
+
+ controller.snapToHalfScreen(
+ task,
+ mockSurface,
+ currentDragBounds,
+ SnapPosition.LEFT,
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.TOUCH,
+ desktopWindowDecoration,
+ )
+ // Assert bounds set to stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.TOUCH,
+ task,
+ expectedBounds.width(),
+ expectedBounds.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+ fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+ // Set up task to already be in snapped-left bounds
+ val bounds =
+ Rect(
+ STABLE_BOUNDS.left,
+ STABLE_BOUNDS.top,
+ STABLE_BOUNDS.right / 2,
+ STABLE_BOUNDS.bottom,
+ )
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo =
+ ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ isResizeable = true
+ }
+
+ // Attempt to snap left again
+ val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+ controller.snapToHalfScreen(
+ task,
+ mockSurface,
+ currentDragBounds,
+ SnapPosition.LEFT,
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.TOUCH,
+ desktopWindowDecoration,
+ )
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator)
+ .start(eq(task.taskId), eq(mockSurface), eq(currentDragBounds), eq(bounds), anyOrNull())
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.TOUCH,
+ task,
+ bounds.width(),
+ bounds.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ @DisableFlags(
+ Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING,
+ Flags.FLAG_ENABLE_TILE_RESIZING,
)
- val arg: ArgumentCaptor<WindowContainerTransaction> =
- ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- var expectedWindowingMode: Int
- if (indicatorType == DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) {
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN
- // Fullscreen launches currently use default transitions
- verify(transitions).startTransition(any(), capture(arg), anyOrNull())
- } else {
- expectedWindowingMode = WINDOWING_MODE_FREEFORM
- // All other launches use a special handler.
- verify(dragAndDropTransitionHandler).handleDropEvent(capture(arg))
- }
- assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
- .launchWindowingMode).isEqualTo(expectedWindowingMode)
- assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
- .launchBounds).isEqualTo(expectedBounds)
- }
-
- private val desktopWallpaperIntent: Intent
- get() = Intent(context, DesktopWallpaperActivity::class.java)
-
- private fun addFreeformTaskAtPosition(
- pos: DesktopTaskPosition,
- stableBounds: Rect,
- bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS,
- offsetPos: Point = Point(0, 0)
- ): RunningTaskInfo {
- val offset = pos.getTopLeftCoordinates(stableBounds, bounds)
- val prevTaskBounds = Rect(bounds)
- prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y)
- return setUpFreeformTask(bounds = prevTaskBounds)
- }
-
- private fun setUpFreeformTask(
- displayId: Int = DEFAULT_DISPLAY,
- bounds: Rect? = null,
- active: Boolean = true,
- background: Boolean = false,
- ): RunningTaskInfo {
- val task = createFreeformTask(displayId, bounds)
- val activityInfo = ActivityInfo()
- task.topActivityInfo = activityInfo
- if (background) {
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(task.taskId))
- .thenReturn(createTaskInfo(task.taskId))
- } else {
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- }
- taskRepository.addTask(displayId, task.taskId, isVisible = active)
- if (!background) {
- runningTasks.add(task)
- }
- return task
- }
-
- private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
- val task = createHomeTask(displayId)
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- runningTasks.add(task)
- return task
- }
-
- private fun setUpFullscreenTask(
- displayId: Int = DEFAULT_DISPLAY,
- isResizable: Boolean = true,
- windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
- deviceOrientation: Int = ORIENTATION_LANDSCAPE,
- screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
- shouldLetterbox: Boolean = false,
- gravity: Int = Gravity.NO_GRAVITY,
- enableUserFullscreenOverride: Boolean = false,
- enableSystemFullscreenOverride: Boolean = false,
- aspectRatioOverrideApplied: Boolean = false
- ): RunningTaskInfo {
- val task = createFullscreenTask(displayId)
- val activityInfo = ActivityInfo()
- activityInfo.screenOrientation = screenOrientation
- activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
- with(task) {
- topActivityInfo = activityInfo
- isResizeable = isResizable
- configuration.orientation = deviceOrientation
- configuration.windowConfiguration.windowingMode = windowingMode
- appCompatTaskInfo.isUserFullscreenOverrideEnabled = enableUserFullscreenOverride
- appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
-
- if (deviceOrientation == ORIENTATION_LANDSCAPE) {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
- } else {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
- }
-
- if (shouldLetterbox) {
- appCompatTaskInfo.setHasMinAspectRatioOverride(aspectRatioOverrideApplied)
- if (deviceOrientation == ORIENTATION_LANDSCAPE &&
- screenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
- // Letterbox to portrait size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
- } else if (deviceOrientation == ORIENTATION_PORTRAIT &&
- screenOrientation == SCREEN_ORIENTATION_LANDSCAPE) {
- // Letterbox to landscape size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ fun handleSnapResizingTaskOnDrag_nonResizable_snapsToHalfScreen() {
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { isResizeable = false }
+ val preDragBounds = Rect(100, 100, 400, 500)
+ val currentDragBounds = Rect(0, 100, 300, 500)
+ val expectedBounds =
+ Rect(
+ STABLE_BOUNDS.left,
+ STABLE_BOUNDS.top,
+ STABLE_BOUNDS.right / 2,
+ STABLE_BOUNDS.bottom,
+ )
+
+ controller.handleSnapResizingTaskOnDrag(
+ task,
+ SnapPosition.LEFT,
+ mockSurface,
+ currentDragBounds,
+ preDragBounds,
+ motionEvent,
+ desktopWindowDecoration,
+ )
+ val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingStarted(
+ ResizeTrigger.DRAG_LEFT,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ task,
+ preDragBounds.width(),
+ preDragBounds.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+ fun handleSnapResizingTaskOnDrag_nonResizable_startsRepositionAnimation() {
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { isResizeable = false }
+ val preDragBounds = Rect(100, 100, 400, 500)
+ val currentDragBounds = Rect(0, 100, 300, 500)
+
+ controller.handleSnapResizingTaskOnDrag(
+ task,
+ SnapPosition.LEFT,
+ mockSurface,
+ currentDragBounds,
+ preDragBounds,
+ motionEvent,
+ desktopWindowDecoration,
+ )
+ verify(mReturnToDragStartAnimator)
+ .start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(preDragBounds),
+ any(),
+ )
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingStarted(any(), any(), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+ fun handleInstantSnapResizingTask_nonResizable_animatorNotStartedAndShowsToast() {
+ val taskBounds = Rect(0, 0, 200, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply { isResizeable = false }
+
+ controller.handleInstantSnapResizingTask(
+ task,
+ SnapPosition.LEFT,
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.MOUSE,
+ desktopWindowDecoration,
+ )
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+ verify(mockToast).show()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+ @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+ fun handleInstantSnapResizingTask_resizable_snapsToHalfScreenAndNotShowToast() {
+ val taskBounds = Rect(0, 0, 200, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply { isResizeable = true }
+ val expectedBounds =
+ Rect(
+ STABLE_BOUNDS.left,
+ STABLE_BOUNDS.top,
+ STABLE_BOUNDS.right / 2,
+ STABLE_BOUNDS.bottom,
+ )
+
+ controller.handleInstantSnapResizingTask(
+ task,
+ SnapPosition.LEFT,
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.MOUSE,
+ desktopWindowDecoration,
+ )
+
+ // Assert bounds set to half of the stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct(taskBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+ verify(mockToast, never()).show()
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingStarted(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.MOUSE,
+ task,
+ taskBounds.width(),
+ taskBounds.height(),
+ displayController,
+ )
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ InputMethod.MOUSE,
+ task,
+ expectedBounds.width(),
+ expectedBounds.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
+ val bounds = Rect(0, 0, 200, 100)
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo =
+ ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
+ appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+ isResizeable = false
+ }
+
+ // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
+ val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
+
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert bounds set to stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ expectedBounds.width(),
+ expectedBounds.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
+ val bounds = Rect(0, 0, 100, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
+
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingEnded(any(), any(), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() {
+ val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
+
+ // Maximize
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+ task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
+
+ // Restore
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert bounds set to last bounds before maximize
+ val wct = getLatestToggleResizeDesktopTaskWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ boundsBeforeMaximize.width(),
+ boundsBeforeMaximize.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
+ val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply { isResizeable = false }
+
+ // Maximize
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+ task.configuration.windowConfiguration.bounds.set(
+ STABLE_BOUNDS.left,
+ boundsBeforeMaximize.top,
+ STABLE_BOUNDS.right,
+ boundsBeforeMaximize.bottom,
+ )
+
+ // Restore
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert bounds set to last bounds before maximize
+ val wct = getLatestToggleResizeDesktopTaskWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ boundsBeforeMaximize.width(),
+ boundsBeforeMaximize.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
+ val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+ val task =
+ setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply { isResizeable = false }
+
+ // Maximize
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+ task.configuration.windowConfiguration.bounds.set(
+ boundsBeforeMaximize.left,
+ STABLE_BOUNDS.top,
+ boundsBeforeMaximize.right,
+ STABLE_BOUNDS.bottom,
+ )
+
+ // Restore
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert bounds set to last bounds before maximize
+ val wct = getLatestToggleResizeDesktopTaskWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ boundsBeforeMaximize.width(),
+ boundsBeforeMaximize.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
+ val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
+
+ // Maximize
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH,
+ ),
+ )
+ task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
+
+ // Restore
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH,
+ ),
+ )
+
+ // Assert last bounds before maximize removed after use
+ assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.MAXIMIZE_BUTTON,
+ InputMethod.TOUCH,
+ task,
+ boundsBeforeMaximize.width(),
+ boundsBeforeMaximize.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ fun onUnhandledDrag_newFreeformIntent() {
+ testOnUnhandledDrag(
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
+ PointF(1200f, 700f),
+ Rect(240, 700, 2160, 1900),
+ )
+ }
+
+ @Test
+ fun onUnhandledDrag_newFreeformIntentSplitLeft() {
+ testOnUnhandledDrag(
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+ PointF(50f, 700f),
+ Rect(0, 0, 500, 1000),
+ )
+ }
+
+ @Test
+ fun onUnhandledDrag_newFreeformIntentSplitRight() {
+ testOnUnhandledDrag(
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+ PointF(2500f, 700f),
+ Rect(500, 0, 1000, 1000),
+ )
+ }
+
+ @Test
+ fun onUnhandledDrag_newFullscreenIntent() {
+ testOnUnhandledDrag(
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ PointF(1200f, 50f),
+ Rect(),
+ )
+ }
+
+ @Test
+ fun shellController_registersUserChangeListener() {
+ verify(shellController, times(2)).addUserChangeListener(any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_inRecentsTransition_noExit() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
+ }
+
+ @Test
+ fun moveTaskToDesktop_background_attemptsImmersiveExit() {
+ val task = setUpFreeformTask(background = true)
+ val wct = WindowContainerTransaction()
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ eq(wct),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+ whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+ controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
+ @Test
+ fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
+ val task = setUpFreeformTask(background = false)
+ val wct = WindowContainerTransaction()
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ eq(wct),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+ whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+ controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
+ @Test
+ fun moveTaskToFront_background_attemptsImmersiveExit() {
+ val task = setUpFreeformTask(background = true)
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ any(),
+ any(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
+ @Test
+ fun moveTaskToFront_foreground_attemptsImmersiveExit() {
+ val task = setUpFreeformTask(background = false)
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
+ )
+ .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ any(),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
+ @Test
+ fun handleRequest_freeformLaunchToDesktop_attemptsImmersiveExit() {
+ markTaskVisible(setUpFreeformTask())
+ val task = setUpFreeformTask()
+ markTaskVisible(task)
+ val binder = Binder()
+
+ controller.handleRequest(binder, createTransition(task))
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
+ }
+
+ @Test
+ fun handleRequest_fullscreenLaunchToDesktop_attemptsImmersiveExit() {
+ setUpFreeformTask()
+ val task = setUpFullscreenTask()
+ val binder = Binder()
+
+ controller.handleRequest(binder, createTransition(task))
+
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
+ val triggerTask = setUpFullscreenTask(displayId = 5)
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = triggerTask.displayId,
+ taskId = triggerTask.taskId,
+ immersive = true,
+ )
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() {
+ val triggerTask = setUpFreeformTask(displayId = 5)
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = triggerTask.displayId,
+ taskId = triggerTask.taskId,
+ immersive = true,
+ )
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() {
+ val triggerTask = setUpFreeformTask(displayId = 5)
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = triggerTask.displayId,
+ taskId = triggerTask.taskId,
+ immersive = false,
+ )
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() {
+ // At least one freeform task to be in a desktop.
+ val existingTask = setUpFreeformTask(displayId = 5)
+ val triggerTask = setUpFullscreenTask(displayId = 5)
+ assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = existingTask.displayId,
+ taskId = existingTask.taskId,
+ immersive = true,
+ )
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
+ val triggerTask = setUpFullscreenTask(displayId = 5)
+ assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() {
+ // At least one freeform task to be in a desktop.
+ val existingTask = setUpFreeformTask(displayId = 5)
+ val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+ assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = existingTask.displayId,
+ taskId = existingTask.taskId,
+ immersive = true,
+ )
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
+ val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+ assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
+
+ assertThat(
+ controller.shouldPlayDesktopAnimation(
+ TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+ )
+ )
+ .isFalse()
+ }
+
+ private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
+ var invocations = 0
+ private set
+
+ var lastInvoked: IBinder? = null
+ private set
+
+ override fun invoke(transition: IBinder) {
+ invocations++
+ lastInvoked = transition
}
- }
- }
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- runningTasks.add(task)
- return task
- }
-
- private fun setUpLandscapeDisplay() {
- whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
- whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
- val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_LONG,
- DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT
- )
- whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(stableBounds)
}
- }
- private fun setUpPortraitDisplay() {
- whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
- whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
- val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_SHORT,
- DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT
- )
- whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(stableBounds)
- }
- }
-
- private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
- val task = createSplitScreenTask(displayId)
- whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
- whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- runningTasks.add(task)
- return task
- }
-
- private fun markTaskVisible(task: RunningTaskInfo) {
- taskRepository.updateTask(task.displayId, task.taskId, isVisible = true)
- }
-
- private fun markTaskHidden(task: RunningTaskInfo) {
- taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
- }
-
- private fun getLatestWct(
- @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
- handlerClass: Class<out TransitionHandler>? = null
- ): WindowContainerTransaction {
- val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- if (handlerClass == null) {
- verify(transitions).startTransition(eq(type), arg.capture(), isNull())
- } else {
- verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
- }
- return arg.value
- }
-
- private fun getLatestToggleResizeDesktopTaskWct(
- currentBounds: Rect? = null
- ): WindowContainerTransaction {
- val arg: ArgumentCaptor<WindowContainerTransaction> =
- ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
- .startTransition(capture(arg), eq(currentBounds))
- return arg.value
- }
-
- private fun getLatestDesktopMixedTaskWct(
- @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
- ): WindowContainerTransaction {
- val arg: ArgumentCaptor<WindowContainerTransaction> =
- ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(desktopMixedTransitionHandler)
- .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull(), anyOrNull())
- return arg.value
- }
-
- private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
- val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
- return arg.value
- }
-
- private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
- val arg: ArgumentCaptor<WindowContainerTransaction> =
- ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
- return arg.value
- }
-
- private fun getLatestExitDesktopWct(): WindowContainerTransaction {
- val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
- verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any())
- return arg.value
- }
-
- private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
- wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
-
- private fun verifyWCTNotExecuted() {
- verify(transitions, never()).startTransition(anyInt(), any(), isNull())
- }
-
- private fun verifyExitDesktopWCTNotExecuted() {
- verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
- }
-
- private fun verifyEnterDesktopWCTNotExecuted() {
- verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
- }
-
- private fun createTransition(
- task: RunningTaskInfo?,
- @WindowManager.TransitionType type: Int = TRANSIT_OPEN
- ): TransitionRequestInfo {
- return TransitionRequestInfo(type, task, null /* remoteTransition */)
- }
-
- private companion object {
- const val SECOND_DISPLAY = 2
- val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
- const val MAX_TASK_LIMIT = 6
- private const val TASKBAR_FRAME_HEIGHT = 200
- }
+ private fun RunOnStartTransitionCallback.assertOnlyInvocation(transition: IBinder) {
+ assertThat(invocations).isEqualTo(1)
+ assertThat(lastInvoked).isEqualTo(transition)
+ }
+
+ /**
+ * Assert that an unhandled drag event launches a PendingIntent with the windowing mode and
+ * bounds we are expecting.
+ */
+ private fun testOnUnhandledDrag(
+ indicatorType: DesktopModeVisualIndicator.IndicatorType,
+ inputCoordinate: PointF,
+ expectedBounds: Rect,
+ ) {
+ setUpLandscapeDisplay()
+ val task = setUpFreeformTask()
+ markTaskVisible(task)
+ task.isFocused = true
+ val runningTasks = ArrayList<RunningTaskInfo>()
+ runningTasks.add(task)
+ val spyController = spy(controller)
+ val mockPendingIntent = mock(PendingIntent::class.java)
+ val mockDragEvent = mock(DragEvent::class.java)
+ val mockCallback = mock(Consumer::class.java)
+ val b = SurfaceControl.Builder()
+ b.setName("test surface")
+ val dragSurface = b.build()
+ whenever(shellTaskOrganizer.runningTasks).thenReturn(runningTasks)
+ whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
+ whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
+ whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
+ whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ doReturn(indicatorType)
+ .whenever(spyController)
+ .updateVisualIndicator(
+ eq(task),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ eq(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT),
+ )
+
+ spyController.onUnhandledDrag(
+ mockPendingIntent,
+ mockDragEvent,
+ mockCallback as Consumer<Boolean>,
+ )
+ val arg: ArgumentCaptor<WindowContainerTransaction> =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ var expectedWindowingMode: Int
+ if (indicatorType == DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) {
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN
+ // Fullscreen launches currently use default transitions
+ verify(transitions).startTransition(any(), capture(arg), anyOrNull())
+ } else {
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM
+ // All other launches use a special handler.
+ verify(dragAndDropTransitionHandler).handleDropEvent(capture(arg))
+ }
+ assertThat(
+ ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
+ .launchWindowingMode
+ )
+ .isEqualTo(expectedWindowingMode)
+ assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions).launchBounds)
+ .isEqualTo(expectedBounds)
+ }
+
+ private val desktopWallpaperIntent: Intent
+ get() = Intent(context, DesktopWallpaperActivity::class.java)
+
+ private fun addFreeformTaskAtPosition(
+ pos: DesktopTaskPosition,
+ stableBounds: Rect,
+ bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS,
+ offsetPos: Point = Point(0, 0),
+ ): RunningTaskInfo {
+ val offset = pos.getTopLeftCoordinates(stableBounds, bounds)
+ val prevTaskBounds = Rect(bounds)
+ prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y)
+ return setUpFreeformTask(bounds = prevTaskBounds)
+ }
+
+ private fun setUpFreeformTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ bounds: Rect? = null,
+ active: Boolean = true,
+ background: Boolean = false,
+ ): RunningTaskInfo {
+ val task = createFreeformTask(displayId, bounds)
+ val activityInfo = ActivityInfo()
+ task.topActivityInfo = activityInfo
+ if (background) {
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(task.taskId))
+ .thenReturn(createTaskInfo(task.taskId))
+ } else {
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ }
+ taskRepository.addTask(displayId, task.taskId, isVisible = active)
+ if (!background) {
+ runningTasks.add(task)
+ }
+ return task
+ }
+
+ private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo {
+ return setUpFreeformTask().apply {
+ pictureInPictureParams =
+ PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
+ }
+ }
+
+ private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createHomeTask(displayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun setUpFullscreenTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ isResizable: Boolean = true,
+ windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+ deviceOrientation: Int = ORIENTATION_LANDSCAPE,
+ screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
+ shouldLetterbox: Boolean = false,
+ gravity: Int = Gravity.NO_GRAVITY,
+ enableUserFullscreenOverride: Boolean = false,
+ enableSystemFullscreenOverride: Boolean = false,
+ aspectRatioOverrideApplied: Boolean = false,
+ ): RunningTaskInfo {
+ val task = createFullscreenTask(displayId)
+ val activityInfo = ActivityInfo()
+ activityInfo.screenOrientation = screenOrientation
+ activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
+ with(task) {
+ topActivityInfo = activityInfo
+ isResizeable = isResizable
+ configuration.orientation = deviceOrientation
+ configuration.windowConfiguration.windowingMode = windowingMode
+ appCompatTaskInfo.isUserFullscreenOverrideEnabled = enableUserFullscreenOverride
+ appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
+
+ if (deviceOrientation == ORIENTATION_LANDSCAPE) {
+ configuration.windowConfiguration.appBounds =
+ Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
+ appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
+ appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+ } else {
+ configuration.windowConfiguration.appBounds =
+ Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
+ appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
+ appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+ }
+
+ if (shouldLetterbox) {
+ appCompatTaskInfo.setHasMinAspectRatioOverride(aspectRatioOverrideApplied)
+ if (
+ deviceOrientation == ORIENTATION_LANDSCAPE &&
+ screenOrientation == SCREEN_ORIENTATION_PORTRAIT
+ ) {
+ // Letterbox to portrait size
+ appCompatTaskInfo.setTopActivityLetterboxed(true)
+ appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
+ appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+ } else if (
+ deviceOrientation == ORIENTATION_PORTRAIT &&
+ screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
+ ) {
+ // Letterbox to landscape size
+ appCompatTaskInfo.setTopActivityLetterboxed(true)
+ appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
+ appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ }
+ }
+ }
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun setUpLandscapeDisplay() {
+ whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
+ whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
+ val stableBounds =
+ Rect(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_LONG,
+ DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT,
+ )
+ whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ }
+
+ private fun setUpPortraitDisplay() {
+ whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
+ whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
+ val stableBounds =
+ Rect(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_SHORT,
+ DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT,
+ )
+ whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ }
+
+ private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createSplitScreenTask(displayId)
+ whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun markTaskVisible(task: RunningTaskInfo) {
+ taskRepository.updateTask(task.displayId, task.taskId, isVisible = true)
+ }
+
+ private fun markTaskHidden(task: RunningTaskInfo) {
+ taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
+ }
+
+ private fun getLatestWct(
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+ handlerClass: Class<out TransitionHandler>? = null,
+ ): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ if (handlerClass == null) {
+ verify(transitions).startTransition(eq(type), arg.capture(), isNull())
+ } else {
+ verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+ }
+ return arg.value
+ }
+
+ private fun getLatestToggleResizeDesktopTaskWct(
+ currentBounds: Rect? = null
+ ): WindowContainerTransaction {
+ val arg: ArgumentCaptor<WindowContainerTransaction> =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
+ .startTransition(capture(arg), eq(currentBounds))
+ return arg.value
+ }
+
+ private fun getLatestDesktopMixedTaskWct(
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN
+ ): WindowContainerTransaction {
+ val arg: ArgumentCaptor<WindowContainerTransaction> =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(desktopMixedTransitionHandler)
+ .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull(), anyOrNull())
+ return arg.value
+ }
+
+ private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
+ return arg.value
+ }
+
+ private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
+ val arg: ArgumentCaptor<WindowContainerTransaction> =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
+ return arg.value
+ }
+
+ private fun getLatestExitDesktopWct(): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any())
+ return arg.value
+ }
+
+ private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+ wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+
+ private fun verifyWCTNotExecuted() {
+ verify(transitions, never()).startTransition(anyInt(), any(), isNull())
+ }
+
+ private fun verifyExitDesktopWCTNotExecuted() {
+ verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
+ }
+
+ private fun verifyEnterDesktopWCTNotExecuted() {
+ verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
+ }
+
+ private fun createTransition(
+ task: RunningTaskInfo?,
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+ ): TransitionRequestInfo {
+ return TransitionRequestInfo(type, task, null /* remoteTransition */)
+ }
+
+ private companion object {
+ const val SECOND_DISPLAY = 2
+ val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
+ const val MAX_TASK_LIMIT = 6
+ private const val TASKBAR_FRAME_HEIGHT = 200
+ }
}
private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
- assertWithMessage("WCT does not have a hierarchy operation at index $index")
- .that(hierarchyOps.size)
- .isGreaterThan(index)
+ assertWithMessage("WCT does not have a hierarchy operation at index $index")
+ .that(hierarchyOps.size)
+ .isGreaterThan(index)
}
private fun WindowContainerTransaction.assertReorderAt(
index: Int,
task: RunningTaskInfo,
- toTop: Boolean? = null
+ toTop: Boolean? = null,
) {
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
- assertThat(op.container).isEqualTo(task.token.asBinder())
- toTop?.let { assertThat(op.toTop).isEqualTo(it) }
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
+ assertThat(op.container).isEqualTo(task.token.asBinder())
+ toTop?.let { assertThat(op.toTop).isEqualTo(it) }
}
private fun WindowContainerTransaction.assertReorderSequence(vararg tasks: RunningTaskInfo) {
- for (i in tasks.indices) {
- assertReorderAt(i, tasks[i])
- }
+ for (i in tasks.indices) {
+ assertReorderAt(i, tasks[i])
+ }
}
/** Checks if the reorder hierarchy operations in [range] correspond to [tasks] list */
private fun WindowContainerTransaction.assertReorderSequenceInRange(
- range: IntRange,
- vararg tasks: RunningTaskInfo
+ range: IntRange,
+ vararg tasks: RunningTaskInfo,
) {
- assertThat(hierarchyOps.slice(range).map { it.type to it.container })
- .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
- .inOrder()
+ assertThat(hierarchyOps.slice(range).map { it.type to it.container })
+ .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
+ .inOrder()
}
private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
- assertThat(op.container).isEqualTo(token.asBinder())
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(op.container).isEqualTo(token.asBinder())
}
private fun WindowContainerTransaction.assertNoRemoveAt(index: Int, token: WindowContainerToken) {
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
- assertThat(op.container).isEqualTo(token.asBinder())
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(op.container).isEqualTo(token.asBinder())
}
private fun WindowContainerTransaction.hasRemoveAt(index: Int, token: WindowContainerToken) {
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
- assertThat(op.container).isEqualTo(token.asBinder())
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(op.container).isEqualTo(token.asBinder())
}
private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
- assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
}
private fun WindowContainerTransaction.assertLaunchTaskAt(
index: Int,
taskId: Int,
- windowingMode: Int
+ windowingMode: Int,
) {
- val keyLaunchWindowingMode = "android.activity.windowingMode"
-
- assertIndexInBounds(index)
- val op = hierarchyOps[index]
- assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
- assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
- assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
- .isEqualTo(windowingMode)
+ val keyLaunchWindowingMode = "android.activity.windowingMode"
+
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+ assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
+ assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
+ .isEqualTo(windowingMode)
}
private fun WindowContainerTransaction?.anyDensityConfigChange(
token: WindowContainerToken
): Boolean {
- return this?.changes?.any { change ->
- change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
- } ?: false
+ return this?.changes?.any { change ->
+ change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
+ } ?: false
}
private fun WindowContainerTransaction?.anyWindowingModeChange(
- token: WindowContainerToken
+ token: WindowContainerToken
): Boolean {
-return this?.changes?.any { change ->
- change.key == token.asBinder() && change.value.windowingMode >= 0
-} ?: false
+ return this?.changes?.any { change ->
+ change.key == token.asBinder() && change.value.windowingMode >= 0
+ } ?: false
}
private fun createTaskInfo(id: Int) =
RecentTaskInfo().apply {
- taskId = id
- token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+ taskId = id
+ token = WindowContainerToken(mock(IWindowContainerToken::class.java))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 0712d58166bb..e6f1fcf7f14f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -47,6 +47,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
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.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
@@ -84,9 +85,7 @@ import org.mockito.quality.Strictness
@ExperimentalCoroutinesApi
class DesktopTasksLimiterTest : ShellTestCase() {
- @JvmField
- @Rule
- val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
@Mock lateinit var transitions: Transitions
@@ -96,6 +95,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Mock lateinit var persistentRepository: DesktopPersistentRepository
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var shellController: ShellController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -106,9 +106,12 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Before
fun setUp() {
- mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
- .spyStatic(DesktopModeStatus::class.java).startMocking()
- doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(any()) }
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
shellInit = spy(ShellInit(testExecutor))
Dispatchers.setMain(StandardTestDispatcher())
testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
@@ -117,15 +120,23 @@ class DesktopTasksLimiterTest : ShellTestCase() {
DesktopUserRepositories(
context,
shellInit,
+ shellController,
persistentRepository,
repositoryInitializer,
testScope,
- userManager
+ userManager,
)
desktopTaskRepo = userRepositories.current
desktopTasksLimiter =
- DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT,
- interactionJankMonitor, mContext, handler)
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ MAX_TASK_LIMIT,
+ interactionJankMonitor,
+ mContext,
+ handler,
+ )
}
@After
@@ -137,16 +148,30 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
- DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, 0,
- interactionJankMonitor, mContext, handler)
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ 0,
+ interactionJankMonitor,
+ mContext,
+ handler,
+ )
}
}
@Test
fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
- DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, -5,
- interactionJankMonitor, mContext, handler)
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ -5,
+ interactionJankMonitor,
+ mContext,
+ handler,
+ )
}
}
@@ -165,11 +190,14 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
Binder() /* transition */,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
}
@@ -181,13 +209,19 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskHidden(task)
desktopTasksLimiter.addPendingMinimizeChange(
- pendingTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ pendingTransition,
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
- taskTransition /* transition */,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ taskTransition /* transition */,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
}
@@ -198,13 +232,19 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskVisible(task)
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ transition,
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).build(),
StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
}
@@ -215,13 +255,19 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskHidden(task)
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ transition,
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).build(),
StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
}
@@ -231,13 +277,19 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ transition,
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
}
@@ -248,22 +300,29 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
- val change = TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
- mode = TRANSIT_TO_BACK
- taskInfo = task
- setStartAbsBounds(bounds)
- }
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
transition,
- TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
+
+ val change =
+ TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
+ mode = TRANSIT_TO_BACK
+ taskInfo = task
+ setStartAbsBounds(bounds)
+ }
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ transition,
+ TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
- assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId)).isEqualTo(
- bounds)
+ assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId))
+ .isEqualTo(bounds)
}
@Test
@@ -272,15 +331,22 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val newTransition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
- desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
- mergedTransition, newTransition)
-
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
- newTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ mergedTransition,
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionMerged(mergedTransition, newTransition)
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ newTransition,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
}
@@ -294,7 +360,9 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
- DEFAULT_DISPLAY, wct)
+ DEFAULT_DISPLAY,
+ wct,
+ )
assertThat(wct.isEmpty).isTrue()
}
@@ -304,7 +372,9 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() {
val wct = WindowContainerTransaction()
desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
- DEFAULT_DISPLAY, wct)
+ DEFAULT_DISPLAY,
+ wct,
+ )
assertThat(wct.isEmpty).isTrue()
}
@@ -319,7 +389,9 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
- DEFAULT_DISPLAY, wct)
+ DEFAULT_DISPLAY,
+ wct,
+ )
assertThat(wct.hierarchyOps).hasSize(2)
assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
@@ -348,10 +420,11 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
- desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = DEFAULT_DISPLAY,
- wct = wct,
- newFrontTaskId = setUpFreeformTask().taskId)
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ displayId = DEFAULT_DISPLAY,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
assertThat(minimizedTaskId).isNull()
assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
@@ -364,10 +437,11 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
- desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = DEFAULT_DISPLAY,
- wct = wct,
- newFrontTaskId = setUpFreeformTask().taskId)
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ displayId = DEFAULT_DISPLAY,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
assertThat(wct.hierarchyOps.size).isEqualTo(1)
@@ -382,10 +456,11 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
- desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = 0,
- wct = wct,
- newFrontTaskId = setUpFreeformTask().taskId)
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ displayId = 0,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
assertThat(minimizedTaskId).isNull()
assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
@@ -395,8 +470,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
- visibleOrderedTasks = tasks.map { it.taskId })
+ val minimizedTask =
+ desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
assertThat(minimizedTask).isNull()
}
@@ -405,8 +480,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
- visibleOrderedTasks = tasks.map { it.taskId })
+ val minimizedTask =
+ desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -415,12 +490,19 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() {
desktopTasksLimiter =
- DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT2,
- interactionJankMonitor, mContext, handler)
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ MAX_TASK_LIMIT2,
+ interactionJankMonitor,
+ mContext,
+ handler,
+ )
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
- visibleOrderedTasks = tasks.map { it.taskId })
+ val minimizedTask =
+ desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -430,9 +512,25 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
+ val minimizedTask =
+ desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId },
- newTaskIdInFront = setUpFreeformTask().taskId)
+ newTaskIdInFront = setUpFreeformTask().taskId,
+ )
+
+ // first == front, last == back
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
+ }
+
+ @Test
+ fun getTaskToMinimize_tasksAtLimit_newIntentReturnsBackTask() {
+ val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+ val minimizedTask =
+ desktopTasksLimiter.getTaskIdToMinimize(
+ visibleOrderedTasks = tasks.map { it.taskId },
+ newTaskIdInFront = null,
+ launchingNewIntent = true,
+ )
// first == front, last == back
assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -444,25 +542,28 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
transition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ transition,
+ TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
- verify(interactionJankMonitor).begin(
- any(),
- eq(mContext),
- eq(handler),
- eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+ verify(interactionJankMonitor)
+ .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
- transition,
- /* aborted = */ false)
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionFinished(transition, /* aborted= */ false)
verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
}
@@ -473,26 +574,28 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
transition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ transition,
+ TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
- verify(interactionJankMonitor).begin(
- any(),
- eq(mContext),
- eq(handler),
- eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW),
- )
+ verify(interactionJankMonitor)
+ .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
- transition,
- /* aborted = */ true)
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionFinished(transition, /* aborted= */ true)
verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
}
@@ -504,25 +607,28 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val newTransition = Binder()
val task = setUpFreeformTask()
desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
- desktopTasksLimiter.getTransitionObserver().onTransitionReady(
mergedTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction() /* finishTransaction */)
+ displayId = DEFAULT_DISPLAY,
+ taskId = task.taskId,
+ )
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ mergedTransition,
+ TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction(), /* finishTransaction */
+ )
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
- verify(interactionJankMonitor).begin(
- any(),
- eq(mContext),
- eq(handler),
- eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+ verify(interactionJankMonitor)
+ .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
- mergedTransition,
- newTransition)
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionMerged(mergedTransition, newTransition)
verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
}
@@ -535,19 +641,11 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
private fun markTaskVisible(task: RunningTaskInfo) {
- desktopTaskRepo.updateTask(
- task.displayId,
- task.taskId,
- isVisible = true
- )
+ desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = true)
}
private fun markTaskHidden(task: RunningTaskInfo) {
- desktopTaskRepo.updateTask(
- task.displayId,
- task.taskId,
- isVisible = false
- )
+ desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = false)
}
private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index b31a3f5fa642..c9623bcd5c16 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -99,7 +99,7 @@ class DesktopTasksTransitionObserverTest {
shellTaskOrganizer,
mixedHandler,
backAnimationController,
- shellInit
+ shellInit,
)
}
@@ -139,7 +139,10 @@ class DesktopTasksTransitionObserverTest {
verify(taskRepository).minimizeTask(task.displayId, task.taskId)
val pendingTransition =
DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
- transition, task.taskId, isLastTask = false)
+ transition,
+ task.taskId,
+ isLastTask = false,
+ )
verify(mixedHandler).addPendingMixedTransition(pendingTransition)
}
@@ -162,7 +165,10 @@ class DesktopTasksTransitionObserverTest {
verify(taskRepository).minimizeTask(task.displayId, task.taskId)
val pendingTransition =
DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
- transition, task.taskId, isLastTask = true)
+ transition,
+ task.taskId,
+ isLastTask = true,
+ )
verify(mixedHandler).addPendingMixedTransition(pendingTransition)
}
@@ -251,7 +257,8 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = task
flags = flags
- })
+ }
+ )
if (withWallpaper) {
addChange(
Change(mock(), mock()).apply {
@@ -259,14 +266,15 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = createWallpaperTaskInfo()
flags = flags
- })
+ }
+ )
}
}
}
private fun createOpenChangeTransition(
task: RunningTaskInfo?,
- type: Int = TRANSIT_OPEN
+ type: Int = TRANSIT_OPEN,
): TransitionInfo {
return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
addChange(
@@ -275,7 +283,8 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = task
flags = flags
- })
+ }
+ )
}
}
@@ -287,13 +296,14 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = task
flags = flags
- })
+ }
+ )
}
}
private fun getLatestWct(
@WindowManager.TransitionType type: Int = TRANSIT_OPEN,
- handlerClass: Class<out Transitions.TransitionHandler>? = null
+ handlerClass: Class<out Transitions.TransitionHandler>? = null,
): WindowContainerTransaction {
val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
if (handlerClass == null) {
@@ -330,8 +340,6 @@ class DesktopTasksTransitionObserverTest {
RunningTaskInfo().apply {
token = mock<WindowContainerToken>()
baseIntent =
- Intent().apply {
- component = DesktopWallpaperActivity.wallpaperActivityComponent
- }
+ Intent().apply { component = DesktopWallpaperActivity.wallpaperActivityComponent }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 866d1b3880b0..aee8821a63f6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -73,10 +73,10 @@ object DesktopTestHelpers {
.setLastActiveTime(100)
.build()
- /** Create a new System Modal task, i.e. a task with a single transparent activity. */
+ /** Create a new System Modal task, i.e. a task with only transparent activities. */
fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo =
createFullscreenTaskBuilder(displayId)
- .setTopActivityTransparent(true)
+ .setActivityStackTransparent(true)
.setNumActivities(1)
.build()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
index 5767df4c5a8e..b9e307fa5973 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
@@ -24,14 +24,15 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_HSUM
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
+import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -66,6 +67,7 @@ class DesktopUserRepositoriesTest : ShellTestCase() {
private val persistentRepository = mock<DesktopPersistentRepository>()
private val repositoryInitializer = mock<DesktopRepositoryInitializer>()
private val userManager = mock<UserManager>()
+ private val shellController = mock<ShellController>()
@Before
fun setUp() {
@@ -80,14 +82,20 @@ class DesktopUserRepositoriesTest : ShellTestCase() {
datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
shellInit = spy(ShellInit(testExecutor))
- val profiles: MutableList<UserInfo> = mutableListOf(
- UserInfo(USER_ID_1, "User 1", 0),
- UserInfo(PROFILE_ID_2, "Profile 2", 0))
+ val profiles: MutableList<UserInfo> =
+ mutableListOf(UserInfo(USER_ID_1, "User 1", 0), UserInfo(PROFILE_ID_2, "Profile 2", 0))
whenever(userManager.getProfiles(USER_ID_1)).thenReturn(profiles)
- userRepositories = DesktopUserRepositories(
- context, shellInit, persistentRepository, repositoryInitializer, datastoreScope,
- userManager)
+ userRepositories =
+ DesktopUserRepositories(
+ context,
+ shellInit,
+ shellController,
+ persistentRepository,
+ repositoryInitializer,
+ datastoreScope,
+ userManager,
+ )
}
@After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 13528b947609..e4eff9f1d592 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -61,9 +61,7 @@ import org.mockito.quality.Strictness
@RunWithLooper
@RunWith(AndroidTestingRunner::class)
class DragToDesktopTransitionHandlerTest : ShellTestCase() {
- @JvmField
- @Rule
- val mAnimatorTestRule = AnimatorTestRule(this)
+ @JvmField @Rule val mAnimatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var transitions: Transitions
@Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@@ -123,11 +121,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
- draggedTask = task
+ draggedTask = task,
),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
verify(dragAnimator).startAnimation()
@@ -137,13 +135,13 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() {
performEarlyCancel(
defaultHandler,
- DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
+ DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
)
verify(transitions)
.startTransition(
eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
any(),
- eq(defaultHandler)
+ eq(defaultHandler),
)
}
@@ -151,7 +149,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
fun startDragToDesktop_cancelledBeforeReady_verifySplitLeftCancel() {
performEarlyCancel(
defaultHandler,
- DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT
+ DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT,
)
verify(splitScreenController)
.requestEnterSplitSelect(any(), any(), eq(SPLIT_POSITION_TOP_OR_LEFT), any())
@@ -161,7 +159,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
fun startDragToDesktop_cancelledBeforeReady_verifySplitRightCancel() {
performEarlyCancel(
defaultHandler,
- DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT
+ DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT,
)
verify(splitScreenController)
.requestEnterSplitSelect(any(), any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any())
@@ -214,7 +212,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
.startTransition(
eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
any(),
- eq(defaultHandler)
+ eq(defaultHandler),
)
}
@@ -277,14 +275,18 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
val startToken = startDrag(defaultHandler)
// Then user cancelled after it had already started.
- val cancelToken = cancelDragToDesktopTransition(
- defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+ val cancelToken =
+ cancelDragToDesktopTransition(
+ defaultHandler,
+ DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
+ )
defaultHandler.mergeAnimation(
cancelToken,
TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
mock<SurfaceControl.Transaction>(),
startToken,
- mock<Transitions.TransitionFinishCallback>())
+ mock<Transitions.TransitionFinishCallback>(),
+ )
// Cancel animation should run since it had already started.
verify(dragAnimator).cancelAnimator()
@@ -296,8 +298,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
val startToken = startDrag(defaultHandler)
// Then user cancelled after it had already started.
- val cancelToken = cancelDragToDesktopTransition(
- defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+ val cancelToken =
+ cancelDragToDesktopTransition(
+ defaultHandler,
+ DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
+ )
defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null)
// Cancel animation should run since it had already started.
@@ -360,7 +365,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
.startTransition(
eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
any(),
- eq(defaultHandler)
+ eq(defaultHandler),
)
}
@@ -374,7 +379,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
.startTransition(
eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP),
any(),
- eq(defaultHandler)
+ eq(defaultHandler),
)
}
@@ -390,7 +395,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info = createTransitionInfo(type = TRANSIT_OPEN, draggedTask = task),
t = transaction,
mergeTarget = mock(),
- finishCallback = finishCallback
+ finishCallback = finishCallback,
)
// Should NOT have any transaction changes
@@ -414,11 +419,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
- draggedTask = task
+ draggedTask = task,
),
t = mergedStartTransaction,
mergeTarget = startTransition,
- finishCallback = finishCallback
+ finishCallback = finishCallback,
)
// Should show dragged task layer in start and finish transaction
@@ -446,11 +451,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
- draggedTask = task
+ draggedTask = task,
),
t = mergedStartTransaction,
mergeTarget = startTransition,
- finishCallback = finishCallback
+ finishCallback = finishCallback,
)
// Should show dragged task layer in start and finish transaction
@@ -475,7 +480,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
assertEquals(
"Expects to return system properties stored value",
/* expected= */ value,
- /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name)
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name),
)
}
@@ -491,7 +496,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
assertEquals(
"Expects to return scaled system properties stored value",
/* expected= */ value / scale,
- /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale)
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale),
)
}
@@ -508,8 +513,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
/* expected= */ defaultValue,
/* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
name,
- default = defaultValue
- )
+ default = defaultValue,
+ ),
)
}
@@ -530,8 +535,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
/* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
name,
default = defaultValue,
- scale = scale
- )
+ scale = scale,
+ ),
)
}
@@ -542,8 +547,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
defaultHandler.onTransitionConsumed(transition, aborted = true, mock())
verify(mockInteractionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD))
- verify(mockInteractionJankMonitor, times(0)).cancel(
- eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE))
+ verify(mockInteractionJankMonitor, times(0))
+ .cancel(eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE))
}
@Test
@@ -554,13 +559,14 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
defaultHandler.onTaskResizeAnimationListener = mock()
defaultHandler.mergeAnimation(
transition = endTransition,
- info = createTransitionInfo(
- type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
- draggedTask = task
- ),
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+ draggedTask = task,
+ ),
t = mock<SurfaceControl.Transaction>(),
mergeTarget = startTransition,
- finishCallback = mock<Transitions.TransitionFinishCallback>()
+ finishCallback = mock<Transitions.TransitionFinishCallback>(),
)
defaultHandler.onTransitionConsumed(endTransition, aborted = true, mock())
@@ -574,7 +580,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
private fun startDrag(
handler: DragToDesktopTransitionHandler,
task: RunningTaskInfo = createTask(),
- finishTransaction: SurfaceControl.Transaction = mock()
+ finishTransaction: SurfaceControl.Transaction = mock(),
): IBinder {
whenever(dragAnimator.position).thenReturn(PointF())
// Simulate transition is started and is ready to animate.
@@ -584,11 +590,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
- draggedTask = task
+ draggedTask = task,
),
startTransaction = mock(),
finishTransaction = finishTransaction,
- finishCallback = {}
+ finishCallback = {},
)
return transition
}
@@ -596,14 +602,14 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
private fun startDragToDesktopTransition(
handler: DragToDesktopTransitionHandler,
task: RunningTaskInfo,
- dragAnimator: MoveToDesktopAnimator
+ dragAnimator: MoveToDesktopAnimator,
): IBinder {
val token = mock<IBinder>()
whenever(
transitions.startTransition(
eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
any(),
- eq(handler)
+ eq(handler),
)
)
.thenReturn(token)
@@ -613,13 +619,14 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
private fun cancelDragToDesktopTransition(
handler: DragToDesktopTransitionHandler,
- cancelState: DragToDesktopTransitionHandler.CancelState): IBinder {
+ cancelState: DragToDesktopTransitionHandler.CancelState,
+ ): IBinder {
val token = mock<IBinder>()
whenever(
transitions.startTransition(
eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
any(),
- eq(handler)
+ eq(handler),
)
)
.thenReturn(token)
@@ -630,7 +637,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
private fun performEarlyCancel(
handler: DragToDesktopTransitionHandler,
- cancelState: DragToDesktopTransitionHandler.CancelState
+ cancelState: DragToDesktopTransitionHandler.CancelState,
) {
val task = createTask()
// Simulate transition is started and is ready to animate.
@@ -643,11 +650,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
info =
createTransitionInfo(
type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
- draggedTask = task
+ draggedTask = task,
),
startTransaction = mock(),
finishTransaction = mock(),
- finishCallback = {}
+ finishCallback = {},
)
// Don't even animate the "drag" since it was already cancelled.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
index 38c6ed90241c..e10253992bb5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
@@ -31,67 +31,71 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class WindowDecorCaptionHandleRepositoryTest {
- private lateinit var captionHandleRepository: WindowDecorCaptionHandleRepository
+ private lateinit var captionHandleRepository: WindowDecorCaptionHandleRepository
- @Before
- fun setUp() {
- captionHandleRepository = WindowDecorCaptionHandleRepository()
- }
+ @Before
+ fun setUp() {
+ captionHandleRepository = WindowDecorCaptionHandleRepository()
+ }
- @Test
- fun initialState_noAction_returnsNoCaption() {
- // Check the initial value of `captionStateFlow`.
- assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
- }
+ @Test
+ fun initialState_noAction_returnsNoCaption() {
+ // Check the initial value of `captionStateFlow`.
+ assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
+ }
- @Test
- fun notifyCaptionChange_toAppHandleVisible_updatesStateWithCorrectData() {
- val taskInfo = createTaskInfo(WINDOWING_MODE_FULLSCREEN, GMAIL_PACKAGE_NAME)
- val appHandleCaptionState =
- CaptionState.AppHandle(
- runningTaskInfo = taskInfo,
- isHandleMenuExpanded = false,
- globalAppHandleBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
- isCapturedLinkAvailable = false)
+ @Test
+ fun notifyCaptionChange_toAppHandleVisible_updatesStateWithCorrectData() {
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FULLSCREEN, GMAIL_PACKAGE_NAME)
+ val appHandleCaptionState =
+ CaptionState.AppHandle(
+ runningTaskInfo = taskInfo,
+ isHandleMenuExpanded = false,
+ globalAppHandleBounds =
+ Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+ isCapturedLinkAvailable = false,
+ )
- captionHandleRepository.notifyCaptionChanged(appHandleCaptionState)
+ captionHandleRepository.notifyCaptionChanged(appHandleCaptionState)
- assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHandleCaptionState)
- }
+ assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHandleCaptionState)
+ }
- @Test
- fun notifyCaptionChange_toAppChipVisible_updatesStateWithCorrectData() {
- val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, GMAIL_PACKAGE_NAME)
- val appHeaderCaptionState =
- CaptionState.AppHeader(
- runningTaskInfo = taskInfo,
- isHeaderMenuExpanded = true,
- globalAppChipBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
- isCapturedLinkAvailable = false)
+ @Test
+ fun notifyCaptionChange_toAppChipVisible_updatesStateWithCorrectData() {
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, GMAIL_PACKAGE_NAME)
+ val appHeaderCaptionState =
+ CaptionState.AppHeader(
+ runningTaskInfo = taskInfo,
+ isHeaderMenuExpanded = true,
+ globalAppChipBounds =
+ Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+ isCapturedLinkAvailable = false,
+ )
- captionHandleRepository.notifyCaptionChanged(appHeaderCaptionState)
+ captionHandleRepository.notifyCaptionChanged(appHeaderCaptionState)
- assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHeaderCaptionState)
- }
+ assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHeaderCaptionState)
+ }
- @Test
- fun notifyCaptionChange_toNoCaption_updatesState() {
- captionHandleRepository.notifyCaptionChanged(CaptionState.NoCaption)
+ @Test
+ fun notifyCaptionChange_toNoCaption_updatesState() {
+ captionHandleRepository.notifyCaptionChanged(CaptionState.NoCaption)
- assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
- }
+ assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
+ }
- private fun createTaskInfo(
- deviceWindowingMode: Int = WINDOWING_MODE_UNDEFINED,
- runningTaskPackageName: String = LAUNCHER_PACKAGE_NAME
- ): RunningTaskInfo =
- RunningTaskInfo().apply {
- configuration.windowConfiguration.apply { windowingMode = deviceWindowingMode }
- topActivityInfo?.apply { packageName = runningTaskPackageName }
- }
+ private fun createTaskInfo(
+ deviceWindowingMode: Int = WINDOWING_MODE_UNDEFINED,
+ runningTaskPackageName: String = LAUNCHER_PACKAGE_NAME,
+ ): RunningTaskInfo =
+ RunningTaskInfo().apply {
+ configuration.windowConfiguration.apply { windowingMode = deviceWindowingMode }
+ topActivityInfo?.apply { packageName = runningTaskPackageName }
+ }
- private companion object {
- const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
- const val LAUNCHER_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
- }
+ private companion object {
+ const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+ const val LAUNCHER_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index b9d7bbf567b7..1569f9dc9b10 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -25,10 +25,10 @@ import android.view.WindowManager.TRANSIT_OPEN
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
-import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
@@ -43,6 +43,10 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+/**
+ * Tests for {@link SystemModalsTransitionHandler} Usage: atest
+ * WMShellUnitTests:SystemModalsTransitionHandlerTest
+ */
@SmallTest
@RunWith(AndroidTestingRunner::class)
class SystemModalsTransitionHandlerTest : ShellTestCase() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
index 9c00c0cee8b1..5475032f35a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
@@ -69,431 +69,448 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@OptIn(ExperimentalCoroutinesApi::class)
class AppHandleEducationControllerTest : ShellTestCase() {
- @JvmField
- @Rule
- val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeStatus::class.java)
- .mockStatic(SystemProperties::class.java)
- .build()!!
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
- private lateinit var educationController: AppHandleEducationController
- private lateinit var testableContext: TestableContext
- private val testScope = TestScope()
- private val testDataStoreFlow = MutableStateFlow(createWindowingEducationProto())
- private val testCaptionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
- private val educationConfigCaptor =
- argumentCaptor<DesktopWindowingEducationTooltipController.TooltipEducationViewConfig>()
- @Mock private lateinit var mockEducationFilter: AppHandleEducationFilter
- @Mock private lateinit var mockDataStoreRepository: AppHandleEducationDatastoreRepository
- @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
- @Mock private lateinit var mockTooltipController: DesktopWindowingEducationTooltipController
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- Dispatchers.setMain(StandardTestDispatcher(testScope.testScheduler))
- testableContext = TestableContext(mContext)
- whenever(mockDataStoreRepository.dataStoreFlow).thenReturn(testDataStoreFlow)
- whenever(mockCaptionHandleRepository.captionStateFlow).thenReturn(testCaptionStateFlow)
- whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
-
- educationController =
- AppHandleEducationController(
- testableContext,
- mockEducationFilter,
- mockDataStoreRepository,
- mockCaptionHandleRepository,
- mockTooltipController,
- testScope.backgroundScope,
- Dispatchers.Main)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleVisible_shouldCallShowEducationTooltip() =
- testScope.runTest {
- // App handle is visible. Should show education tooltip.
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(DesktopModeStatus::class.java)
+ .mockStatic(SystemProperties::class.java)
+ .build()!!
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ private lateinit var educationController: AppHandleEducationController
+ private lateinit var testableContext: TestableContext
+ private val testScope = TestScope()
+ private val testDataStoreFlow = MutableStateFlow(createWindowingEducationProto())
+ private val testCaptionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
+ private val educationConfigCaptor =
+ argumentCaptor<DesktopWindowingEducationTooltipController.TooltipEducationViewConfig>()
+ @Mock private lateinit var mockEducationFilter: AppHandleEducationFilter
+ @Mock private lateinit var mockDataStoreRepository: AppHandleEducationDatastoreRepository
+ @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
+ @Mock private lateinit var mockTooltipController: DesktopWindowingEducationTooltipController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ Dispatchers.setMain(StandardTestDispatcher(testScope.testScheduler))
+ testableContext = TestableContext(mContext)
+ whenever(mockDataStoreRepository.dataStoreFlow).thenReturn(testDataStoreFlow)
+ whenever(mockCaptionHandleRepository.captionStateFlow).thenReturn(testCaptionStateFlow)
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+
+ educationController =
+ AppHandleEducationController(
+ testableContext,
+ mockEducationFilter,
+ mockDataStoreRepository,
+ mockCaptionHandleRepository,
+ mockTooltipController,
+ testScope.backgroundScope,
+ Dispatchers.Main,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHandleVisible_shouldCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle is visible. Should show education tooltip.
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_flagDisabled_shouldNotCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle visible but education aconfig flag disabled, should not show education
+ // tooltip.
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle is visible but [shouldShowAppHandleEducation] api returns false, should
+ // not
+ // show education tooltip.
+ setShouldShowAppHandleEducation(false)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle is not visible, should not show education tooltip.
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle is not visible.
+ testCaptionStateFlow.value = CaptionState.NoCaption
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle is visible but app handle hint has been viewed before,
+ // should not show education tooltip.
+ // Mark app handle hint viewed.
+ testDataStoreFlow.value =
+ createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
+ testScope.runTest {
+ // App handle is visible but app handle hint has been viewed before.
+ // But as we are overriding prerequisite conditions, we should show app
+ // handle tooltip.
+ // Mark app handle hint viewed.
+ testDataStoreFlow.value =
+ createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+ val systemPropertiesKey =
+ "persist.desktop_windowing_app_handle_education_override_conditions"
+ whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+ .thenReturn(true)
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
+ testScope.runTest {
+ setShouldShowAppHandleEducation(false)
+
+ // Simulate app handle visible and expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ // Wait for some time before verifying
+ waitForBufferDelay()
+
+ verify(mockDataStoreRepository, times(1))
+ .updateAppHandleHintUsedTimestampMillis(eq(true))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
+ testScope.runTest {
+ // App handle is visible. Should show education tooltip.
+ setShouldShowAppHandleEducation(true)
+
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockDataStoreRepository, times(1))
+ .updateAppHandleHintViewedTimestampMillis(eq(true))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
+ testScope.runTest {
+ // After first tooltip is dismissed, app handle is expanded. Should show second
+ // education
+ // tooltip.
+ showAndDismissFirstTooltip()
+
+ // Simulate app handle expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ // [showEducationTooltip] should be called twice, once for each tooltip.
+ verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
+ testScope.runTest {
+ // After first tooltip is dismissed, app handle is expanded after timeout. Should not
+ // show
+ // second education tooltip.
+ showAndDismissFirstTooltip()
+
+ // Wait for timeout to occur, after this timeout we should not listen for further
+ // triggers
+ // anymore.
+ advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
+ runCurrent()
+ // Simulate app handle expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ // [showEducationTooltip] should be called once, just for the first tooltip.
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
+ testScope.runTest {
+ // After first tooltip is dismissed, app handle is expanded twice. Should show second
+ // education tooltip only once.
+ showAndDismissFirstTooltip()
+
+ // Simulate app handle expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+ // Simulate app handle being expanded twice.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ waitForBufferDelay()
+
+ // [showEducationTooltip] should not be called thrice, even if app handle was expanded
+ // twice. Should be called twice, once for each tooltip.
+ verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
+ testScope.runTest {
+ // After first tooltip is dismissed, app handle is not expanded. Should not show second
+ // education tooltip.
+ showAndDismissFirstTooltip()
+
+ // Simulate app handle visible but not expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ // [showEducationTooltip] should be called once, just for the first tooltip.
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
+ testScope.runTest {
+ // After first two tooltips are dismissed, app header is visible. Should show third
+ // education tooltip.
+ showAndDismissFirstTooltip()
+ showAndDismissSecondTooltip()
+
+ // Simulate app header visible.
+ testCaptionStateFlow.value = createAppHeaderState()
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
+ testScope.runTest {
+ // After first two tooltips are dismissed, app header is visible after timeout. Should
+ // not
+ // show third education tooltip.
+ showAndDismissFirstTooltip()
+ showAndDismissSecondTooltip()
+
+ // Wait for timeout to occur, after this timeout we should not listen for further
+ // triggers
+ // anymore.
+ advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
+ runCurrent()
+ // Simulate app header visible.
+ testCaptionStateFlow.value = createAppHeaderState()
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
+ testScope.runTest {
+ // After first two tooltips are dismissed, app header is visible twice. Should show
+ // third
+ // education tooltip only once.
+ showAndDismissFirstTooltip()
+ showAndDismissSecondTooltip()
+
+ // Simulate app header visible.
+ testCaptionStateFlow.value = createAppHeaderState()
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+ testCaptionStateFlow.value = createAppHeaderState()
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
+ testScope.runTest {
+ // After first two tooltips are dismissed, app header is visible but expanded. Should
+ // not
+ // show third education tooltip.
+ showAndDismissFirstTooltip()
+ showAndDismissSecondTooltip()
+
+ // Simulate app header visible.
+ testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
+ testScope.runTest {
+ // App handle is visible. Should show education tooltip.
+ setShouldShowAppHandleEducation(true)
+ val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+ val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+ educationController.setAppHandleEducationTooltipCallbacks(
+ mockOpenHandleMenuCallback,
+ mockToDesktopModeCallback,
+ )
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, atLeastOnce())
+ .showEducationTooltip(educationConfigCaptor.capture(), any())
+ educationConfigCaptor.lastValue.onEducationClickAction.invoke()
+
+ verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ @Ignore("b/371527084: revisit testcase after refactoring original logic")
+ fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
+ testScope.runTest {
+ // After first tooltip is dismissed, app handle is expanded. Should show second
+ // education
+ // tooltip.
+ showAndDismissFirstTooltip()
+ val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+ val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+ educationController.setAppHandleEducationTooltipCallbacks(
+ mockOpenHandleMenuCallback,
+ mockToDesktopModeCallback,
+ )
+ // Simulate app handle expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ // Wait for next tooltip to showup.
+ waitForBufferDelay()
+
+ verify(mockTooltipController, atLeastOnce())
+ .showEducationTooltip(educationConfigCaptor.capture(), any())
+ educationConfigCaptor.lastValue.onEducationClickAction.invoke()
+
+ verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
+ }
+
+ private suspend fun TestScope.showAndDismissFirstTooltip() {
setShouldShowAppHandleEducation(true)
-
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_flagDisabled_shouldNotCallShowEducationTooltip() =
- testScope.runTest {
- // App handle visible but education aconfig flag disabled, should not show education
- // tooltip.
- whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
- setShouldShowAppHandleEducation(true)
-
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, never()).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
- testScope.runTest {
- // App handle is visible but [shouldShowAppHandleEducation] api returns false, should not
- // show education tooltip.
- setShouldShowAppHandleEducation(false)
-
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, never()).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
- testScope.runTest {
- // App handle is not visible, should not show education tooltip.
- setShouldShowAppHandleEducation(true)
-
- // Simulate app handle is not visible.
- testCaptionStateFlow.value = CaptionState.NoCaption
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, never()).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() =
- testScope.runTest {
- // App handle is visible but app handle hint has been viewed before,
- // should not show education tooltip.
- // Mark app handle hint viewed.
- testDataStoreFlow.value =
- createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
- setShouldShowAppHandleEducation(true)
-
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, never()).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
- testScope.runTest {
- // App handle is visible but app handle hint has been viewed before.
- // But as we are overriding prerequisite conditions, we should show app
- // handle tooltip.
- // Mark app handle hint viewed.
- testDataStoreFlow.value =
- createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
- val systemPropertiesKey =
- "persist.desktop_windowing_app_handle_education_override_conditions"
- whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
- .thenReturn(true)
- setShouldShowAppHandleEducation(true)
-
// Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
// Wait for first tooltip to showup.
waitForBufferDelay()
-
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
- testScope.runTest {
+ // [shouldShowAppHandleEducation] should return false as education has been viewed
+ // before.
setShouldShowAppHandleEducation(false)
+ // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
+ captureAndInvokeOnDismissAction()
+ }
- // Simulate app handle visible and expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for some time before verifying
- waitForBufferDelay()
-
- verify(mockDataStoreRepository, times(1)).updateAppHandleHintUsedTimestampMillis(eq(true))
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
- testScope.runTest {
- // App handle is visible. Should show education tooltip.
- setShouldShowAppHandleEducation(true)
-
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockDataStoreRepository, times(1)).updateAppHandleHintViewedTimestampMillis(eq(true))
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded. Should show second education
- // tooltip.
- showAndDismissFirstTooltip()
-
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- // [showEducationTooltip] should be called twice, once for each tooltip.
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded after timeout. Should not show
- // second education tooltip.
- showAndDismissFirstTooltip()
-
- // Wait for timeout to occur, after this timeout we should not listen for further triggers
- // anymore.
- advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
- runCurrent()
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- // [showEducationTooltip] should be called once, just for the first tooltip.
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded twice. Should show second
- // education tooltip only once.
- showAndDismissFirstTooltip()
-
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- // Simulate app handle being expanded twice.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- waitForBufferDelay()
-
- // [showEducationTooltip] should not be called thrice, even if app handle was expanded
- // twice. Should be called twice, once for each tooltip.
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is not expanded. Should not show second
- // education tooltip.
- showAndDismissFirstTooltip()
-
- // Simulate app handle visible but not expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- // [showEducationTooltip] should be called once, just for the first tooltip.
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible. Should show third
- // education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible after timeout. Should not
- // show third education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Wait for timeout to occur, after this timeout we should not listen for further triggers
- // anymore.
- advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
- runCurrent()
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible twice. Should show third
- // education tooltip only once.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible but expanded. Should not
- // show third education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
- testScope.runTest {
- // App handle is visible. Should show education tooltip.
- setShouldShowAppHandleEducation(true)
- val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
- val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
- educationController.setAppHandleEducationTooltipCallbacks(
- mockOpenHandleMenuCallback, mockToDesktopModeCallback)
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, atLeastOnce())
- .showEducationTooltip(educationConfigCaptor.capture(), any())
- educationConfigCaptor.lastValue.onEducationClickAction.invoke()
-
- verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded. Should show second education
- // tooltip.
- showAndDismissFirstTooltip()
- val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
- val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
- educationController.setAppHandleEducationTooltipCallbacks(
- mockOpenHandleMenuCallback, mockToDesktopModeCallback)
+ private fun TestScope.showAndDismissSecondTooltip() {
// Simulate app handle expanded.
testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
// Wait for next tooltip to showup.
waitForBufferDelay()
+ // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
+ captureAndInvokeOnDismissAction()
+ }
+ private fun captureAndInvokeOnDismissAction() {
verify(mockTooltipController, atLeastOnce())
.showEducationTooltip(educationConfigCaptor.capture(), any())
- educationConfigCaptor.lastValue.onEducationClickAction.invoke()
-
- verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
- }
-
- private suspend fun TestScope.showAndDismissFirstTooltip() {
- setShouldShowAppHandleEducation(true)
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for first tooltip to showup.
- waitForBufferDelay()
- // [shouldShowAppHandleEducation] should return false as education has been viewed
- // before.
- setShouldShowAppHandleEducation(false)
- // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
- captureAndInvokeOnDismissAction()
- }
-
- private fun TestScope.showAndDismissSecondTooltip() {
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
- captureAndInvokeOnDismissAction()
- }
-
- private fun captureAndInvokeOnDismissAction() {
- verify(mockTooltipController, atLeastOnce())
- .showEducationTooltip(educationConfigCaptor.capture(), any())
- educationConfigCaptor.lastValue.onDismissAction.invoke()
- }
-
- private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
- whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
- .thenReturn(shouldShowAppHandleEducation)
-
- /**
- * Class under test waits for some time before showing education, simulate advance time before
- * verifying or moving forward
- */
- private fun TestScope.waitForBufferDelay() {
- advanceTimeBy(APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS)
- runCurrent()
- }
-
- private companion object {
- val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long = APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
- val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
- APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
- }
+ educationConfigCaptor.lastValue.onDismissAction.invoke()
+ }
+
+ private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
+ whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
+ .thenReturn(shouldShowAppHandleEducation)
+
+ /**
+ * Class under test waits for some time before showing education, simulate advance time before
+ * verifying or moving forward
+ */
+ private fun TestScope.waitForBufferDelay() {
+ advanceTimeBy(APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS)
+ runCurrent()
+ }
+
+ private companion object {
+ val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long =
+ APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
+ val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
+ APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
index 963890d1caa4..4db883d13551 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
@@ -49,85 +49,89 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@ExperimentalCoroutinesApi
class AppHandleEducationDatastoreRepositoryTest {
- private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
- private lateinit var testDatastore: DataStore<WindowingEducationProto>
- private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
- private lateinit var datastoreScope: CoroutineScope
-
- @Before
- fun setUp() {
- Dispatchers.setMain(StandardTestDispatcher())
- datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
- testDatastore =
- DataStoreFactory.create(
- serializer =
- AppHandleEducationDatastoreRepository.Companion.WindowingEducationProtoSerializer,
- scope = datastoreScope) {
- testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
+ private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+ private lateinit var testDatastore: DataStore<WindowingEducationProto>
+ private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+ private lateinit var datastoreScope: CoroutineScope
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(StandardTestDispatcher())
+ datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+ testDatastore =
+ DataStoreFactory.create(
+ serializer =
+ AppHandleEducationDatastoreRepository.Companion
+ .WindowingEducationProtoSerializer,
+ scope = datastoreScope,
+ ) {
+ testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
}
- datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
- }
-
- @After
- fun tearDown() {
- File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
- .deleteRecursively()
-
- datastoreScope.cancel()
- }
-
- @Test
- fun getWindowingEducationProto_returnsCorrectProto() =
- runTest(StandardTestDispatcher()) {
- val windowingEducationProto =
- createWindowingEducationProto(
- appHandleHintViewedTimestampMillis = 123L,
- appHandleHintUsedTimestampMillis = 124L,
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
- appUsageStatsLastUpdateTimestampMillis = 125L)
- testDatastore.updateData { windowingEducationProto }
-
- val resultProto = datastoreRepository.windowingEducationProto()
-
- assertThat(resultProto).isEqualTo(windowingEducationProto)
- }
-
- @Test
- fun updateAppUsageStats_updatesDatastoreProto() =
- runTest(StandardTestDispatcher()) {
- val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3)
- val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L)
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = appUsageStats,
- appUsageStatsLastUpdateTimestampMillis =
- appUsageStatsLastUpdateTimestamp.toMillis())
-
- datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp)
-
- val result = testDatastore.data.first()
- assertThat(result).isEqualTo(windowingEducationProto)
- }
-
- @Test
- fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() =
- runTest(StandardTestDispatcher()) {
- datastoreRepository.updateAppHandleHintViewedTimestampMillis(true)
-
- val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis()
- assertThat(result).isEqualTo(true)
- }
-
- @Test
- fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
- runTest(StandardTestDispatcher()) {
- datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
-
- val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis()
- assertThat(result).isEqualTo(true)
- }
-
- companion object {
- private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
- }
+ datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
+ }
+
+ @After
+ fun tearDown() {
+ File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+ .deleteRecursively()
+
+ datastoreScope.cancel()
+ }
+
+ @Test
+ fun getWindowingEducationProto_returnsCorrectProto() =
+ runTest(StandardTestDispatcher()) {
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appHandleHintViewedTimestampMillis = 123L,
+ appHandleHintUsedTimestampMillis = 124L,
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+ appUsageStatsLastUpdateTimestampMillis = 125L,
+ )
+ testDatastore.updateData { windowingEducationProto }
+
+ val resultProto = datastoreRepository.windowingEducationProto()
+
+ assertThat(resultProto).isEqualTo(windowingEducationProto)
+ }
+
+ @Test
+ fun updateAppUsageStats_updatesDatastoreProto() =
+ runTest(StandardTestDispatcher()) {
+ val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3)
+ val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L)
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = appUsageStats,
+ appUsageStatsLastUpdateTimestampMillis =
+ appUsageStatsLastUpdateTimestamp.toMillis(),
+ )
+
+ datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp)
+
+ val result = testDatastore.data.first()
+ assertThat(result).isEqualTo(windowingEducationProto)
+ }
+
+ @Test
+ fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() =
+ runTest(StandardTestDispatcher()) {
+ datastoreRepository.updateAppHandleHintViewedTimestampMillis(true)
+
+ val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis()
+ assertThat(result).isEqualTo(true)
+ }
+
+ @Test
+ fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
+ runTest(StandardTestDispatcher()) {
+ datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
+
+ val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis()
+ assertThat(result).isEqualTo(true)
+ }
+
+ companion object {
+ private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
index e5edd69155b5..2fc36efb1a41 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
@@ -53,189 +53,221 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class AppHandleEducationFilterTest : ShellTestCase() {
- @JvmField
- @Rule
- val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!!
- @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
- @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
- private lateinit var educationFilter: AppHandleEducationFilter
- private lateinit var testableResources: TestableResources
- private lateinit var testableContext: TestableContext
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- testableContext = TestableContext(mContext)
- testableResources =
- testableContext.orCreateTestableResources.apply {
- addOverride(
- R.array.desktop_windowing_app_handle_education_allowlist_apps,
- arrayOf(GMAIL_PACKAGE_NAME))
- addOverride(R.integer.desktop_windowing_education_required_time_since_setup_seconds, 0)
- addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
- addOverride(
- R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, MAX_VALUE)
- addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100)
- }
- testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager)
- educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository)
- }
-
- @Test
- fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
- // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
- // should return true
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isTrue()
- }
-
- @Test
- fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
- // Pass Youtube as current focus app, it is not in allowlist hence #shouldShowAppHandleEducation
- // should return false
- testableResources.addOverride(
- R.array.desktop_windowing_app_handle_education_allowlist_apps, arrayOf(GMAIL_PACKAGE_NAME))
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- val captionState =
- createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(captionState)
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
- // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation should
- // return false
- testableResources.addOverride(
- R.integer.desktop_windowing_education_required_time_since_setup_seconds, MAX_VALUE)
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
- // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return false
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appHandleHintViewedTimestampMillis = 123L,
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
- // App handle hint has been used before, hence #shouldShowAppHandleEducation should return false
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appHandleHintUsedTimestampMillis = 123L,
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
- // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence
- // #shouldShowAppHandleEducation should return false
- testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
- // UsageStats caching interval is set to 0ms, that means caching should happen very frequently
- testableResources.addOverride(
- R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, 0)
- // The DataStore currently holds a proto object where Gmail's app launch count is recorded as 4.
- // This value exceeds the minimum required count of 3.
- testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = 0)
- // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail.
- // This value is below the minimum required count of 3.
- `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong()))
- .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- // Result should be false as queried usage stats should be considered to determine the result
- // instead of cached stats
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- // Simulate app handle menu is expanded
- val captionState = createAppHandleState(isHandleMenuExpanded = true)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(captionState)
-
- // We should not show app handle education if app menu is expanded
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
- // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence
- // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
- // conditions, #shouldShowAppHandleEducation should return true.
- testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
- val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions"
- whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())).thenReturn(true)
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isTrue()
- }
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!!
+ @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+ @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
+ private lateinit var educationFilter: AppHandleEducationFilter
+ private lateinit var testableResources: TestableResources
+ private lateinit var testableContext: TestableContext
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testableContext = TestableContext(mContext)
+ testableResources =
+ testableContext.orCreateTestableResources.apply {
+ addOverride(
+ R.array.desktop_windowing_app_handle_education_allowlist_apps,
+ arrayOf(GMAIL_PACKAGE_NAME),
+ )
+ addOverride(
+ R.integer.desktop_windowing_education_required_time_since_setup_seconds,
+ 0,
+ )
+ addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+ addOverride(
+ R.integer.desktop_windowing_education_app_usage_cache_interval_seconds,
+ MAX_VALUE,
+ )
+ addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100)
+ }
+ testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager)
+ educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository)
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
+ // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
+ // should return true
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
+ // Pass Youtube as current focus app, it is not in allowlist hence
+ // #shouldShowAppHandleEducation
+ // should return false
+ testableResources.addOverride(
+ R.array.desktop_windowing_app_handle_education_allowlist_apps,
+ arrayOf(GMAIL_PACKAGE_NAME),
+ )
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ val captionState =
+ createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(captionState)
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
+ // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation
+ // should
+ // return false
+ testableResources.addOverride(
+ R.integer.desktop_windowing_education_required_time_since_setup_seconds,
+ MAX_VALUE,
+ )
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
+ // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return
+ // false
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appHandleHintViewedTimestampMillis = 123L,
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
+ // App handle hint has been used before, hence #shouldShowAppHandleEducation should return
+ // false
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appHandleHintUsedTimestampMillis = 123L,
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
+ // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
+ // hence
+ // #shouldShowAppHandleEducation should return false
+ testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
+ // UsageStats caching interval is set to 0ms, that means caching should happen very
+ // frequently
+ testableResources.addOverride(
+ R.integer.desktop_windowing_education_app_usage_cache_interval_seconds,
+ 0,
+ )
+ // The DataStore currently holds a proto object where Gmail's app launch count is recorded
+ // as 4.
+ // This value exceeds the minimum required count of 3.
+ testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appUsageStatsLastUpdateTimestampMillis = 0,
+ )
+ // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail.
+ // This value is below the minimum required count of 3.
+ `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong()))
+ .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ // Result should be false as queried usage stats should be considered to determine the
+ // result
+ // instead of cached stats
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ // Simulate app handle menu is expanded
+ val captionState = createAppHandleState(isHandleMenuExpanded = true)
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(captionState)
+
+ // We should not show app handle education if app menu is expanded
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
+ // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
+ // hence
+ // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
+ // conditions, #shouldShowAppHandleEducation should return true.
+ testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+ val systemPropertiesKey =
+ "persist.desktop_windowing_app_handle_education_override_conditions"
+ whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+ .thenReturn(true)
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+ appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+ )
+ `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+ val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+
+ assertThat(result).isTrue()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
index 6a5d9f67e4a7..8d7fb5d7af85 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
@@ -66,16 +66,27 @@ class DesktopWindowLimitRemoteHandlerTest {
private fun createRemoteHandler(taskIdToMinimize: Int) =
DesktopWindowLimitRemoteHandler(
- shellExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
+ shellExecutor,
+ rootTaskDisplayAreaOrganizer,
+ remoteTransition,
+ taskIdToMinimize,
+ )
@Test
fun startAnimation_dontSetTransition_returnsFalse() {
val minimizeTask = createDesktopTask()
val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
- assertThat(remoteHandler.startAnimation(transition,
- createMinimizeTransitionInfo(minimizeTask), startT, finishT, finishCallback)
- ).isFalse()
+ assertThat(
+ remoteHandler.startAnimation(
+ transition,
+ createMinimizeTransitionInfo(minimizeTask),
+ startT,
+ finishT,
+ finishCallback,
+ )
+ )
+ .isFalse()
}
@Test
@@ -84,9 +95,8 @@ class DesktopWindowLimitRemoteHandlerTest {
remoteHandler.setTransition(transition)
val info = createToFrontTransitionInfo()
- assertThat(
- remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
- ).isFalse()
+ assertThat(remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback))
+ .isFalse()
}
@Test
@@ -96,9 +106,8 @@ class DesktopWindowLimitRemoteHandlerTest {
remoteHandler.setTransition(transition)
val info = createMinimizeTransitionInfo(minimizeTask)
- assertThat(
- remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
- ).isTrue()
+ assertThat(remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback))
+ .isTrue()
}
@Test
@@ -109,8 +118,7 @@ class DesktopWindowLimitRemoteHandlerTest {
remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
- verify(rootTaskDisplayAreaOrganizer, times(0))
- .reparentToDisplayArea(anyInt(), any(), any())
+ verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
}
@Test
@@ -154,14 +162,18 @@ class DesktopWindowLimitRemoteHandlerTest {
private fun createToFrontTransitionInfo() =
TransitionInfoBuilder(TRANSIT_TO_FRONT)
- .addChange(TRANSIT_TO_FRONT,
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+ .addChange(
+ TRANSIT_TO_FRONT,
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build(),
+ )
.build()
private fun createMinimizeTransitionInfo(minimizeTask: ActivityManager.RunningTaskInfo) =
TransitionInfoBuilder(TRANSIT_TO_FRONT)
- .addChange(TRANSIT_TO_FRONT,
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+ .addChange(
+ TRANSIT_TO_FRONT,
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build(),
+ )
.addChange(TRANSIT_TO_BACK, minimizeTask)
.build()
}
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 4f7e80cf8330..eae206664021 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
@@ -63,9 +63,10 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
DataStoreFactory.create(
serializer =
DesktopPersistentRepository.Companion.DesktopPersistentRepositoriesSerializer,
- scope = datastoreScope) {
- testContext.dataStoreFile(DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE)
- }
+ scope = datastoreScope,
+ ) {
+ testContext.dataStoreFile(DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE)
+ }
datastoreRepository = DesktopPersistentRepository(testDatastore)
}
@@ -113,7 +114,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
- userId = DEFAULT_USER_ID)
+ userId = DEFAULT_USER_ID,
+ )
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap).hasSize(2)
@@ -137,7 +139,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
- userId = DEFAULT_USER_ID)
+ userId = DEFAULT_USER_ID,
+ )
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState)
@@ -161,7 +164,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
- userId = DEFAULT_USER_ID)
+ userId = DEFAULT_USER_ID,
+ )
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap).isEmpty()
@@ -194,7 +198,7 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
fun createDesktopTask(
taskId: Int,
- state: DesktopTaskState = DesktopTaskState.VISIBLE
+ state: DesktopTaskState = DesktopTaskState.VISIBLE,
): DesktopTask =
DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
}
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 1c88a290d677..a3c441698905 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_DESKTOP_WINDOWING_PERSISTENCE
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.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -46,15 +47,12 @@ import org.mockito.Mockito.spy
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-
@SmallTest
@RunWith(AndroidTestingRunner::class)
@ExperimentalCoroutinesApi
class DesktopRepositoryInitializerTest : ShellTestCase() {
- @JvmField
- @Rule
- val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
private lateinit var repositoryInitializer: DesktopRepositoryInitializer
private lateinit var shellInit: ShellInit
@@ -64,6 +62,7 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
private val persistentRepository = mock<DesktopPersistentRepository>()
private val userManager = mock<UserManager>()
private val testExecutor = mock<ShellExecutor>()
+ private val shellController = mock<ShellController>()
@Before
fun setUp() {
@@ -74,8 +73,13 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
DesktopRepositoryInitializerImpl(context, persistentRepository, datastoreScope)
desktopUserRepositories =
DesktopUserRepositories(
- context, shellInit, persistentRepository, repositoryInitializer, datastoreScope,
- userManager
+ context,
+ shellInit,
+ shellController,
+ persistentRepository,
+ repositoryInitializer,
+ datastoreScope,
+ userManager,
)
}
@@ -83,101 +87,94 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
fun initWithPersistence_multipleUsers_addedCorrectly() =
runTest(StandardTestDispatcher()) {
- whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
- mapOf(
- USER_ID_1 to desktopRepositoryState1,
- USER_ID_2 to desktopRepositoryState2
+ whenever(persistentRepository.getUserDesktopRepositoryMap())
+ .thenReturn(
+ mapOf(
+ USER_ID_1 to desktopRepositoryState1,
+ USER_ID_2 to desktopRepositoryState2,
+ )
)
- )
whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
.thenReturn(desktopRepositoryState1)
whenever(persistentRepository.getDesktopRepositoryState(USER_ID_2))
.thenReturn(desktopRepositoryState2)
- whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1))
- .thenReturn(desktop1)
- whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2))
- .thenReturn(desktop2)
- whenever(persistentRepository.readDesktop(USER_ID_2, DESKTOP_ID_3))
- .thenReturn(desktop3)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
+ whenever(persistentRepository.readDesktop(USER_ID_2, DESKTOP_ID_3)).thenReturn(desktop3)
repositoryInitializer.initialize(desktopUserRepositories)
// Desktop Repository currently returns all tasks across desktops for a specific user
- // since the repository currently doesn't handle desktops. This test logic should be updated
+ // since the repository currently doesn't handle desktops. This test logic should be
+ // updated
// once the repository handles multiple desktops.
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getActiveTasks(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+ )
.containsExactly(1, 3, 4, 5)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
.containsExactly(5, 1)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getMinimizedTasks(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+ )
.containsExactly(3, 4)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_2)
- .getActiveTasks(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories.getProfile(USER_ID_2).getActiveTasks(DEFAULT_DISPLAY)
+ )
.containsExactly(7, 8)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_2)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories
+ .getProfile(USER_ID_2)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
.contains(7)
assertThat(
- desktopUserRepositories.getProfile(USER_ID_2)
- .getMinimizedTasks(DEFAULT_DISPLAY)
- ).containsExactly(8)
+ desktopUserRepositories.getProfile(USER_ID_2).getMinimizedTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(8)
}
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
fun initWithPersistence_singleUser_addedCorrectly() =
runTest(StandardTestDispatcher()) {
- whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
- mapOf(
- USER_ID_1 to desktopRepositoryState1,
- )
- )
+ 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)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
repositoryInitializer.initialize(desktopUserRepositories)
// Desktop Repository currently returns all tasks across desktops for a specific user
- // since the repository currently doesn't handle desktops. This test logic should be updated
+ // since the repository currently doesn't handle desktops. This test logic should be
+ // updated
// once the repository handles multiple desktops.
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getActiveTasks(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+ )
.containsExactly(1, 3, 4, 5)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
.containsExactly(5, 1)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1)
- .getMinimizedTasks(DEFAULT_DISPLAY)
- )
+ desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+ )
.containsExactly(3, 4)
.inOrder()
}
@@ -195,70 +192,73 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
const val DESKTOP_ID_3 = 4
val freeformTasksInZOrder1 = listOf(1, 3)
- val desktop1: Desktop = Desktop.newBuilder()
- .setDesktopId(DESKTOP_ID_1)
- .addAllZOrderedTasks(freeformTasksInZOrder1)
- .putTasksByTaskId(
- 1,
- DesktopTask.newBuilder()
- .setTaskId(1)
- .setDesktopTaskState(DesktopTaskState.VISIBLE)
- .build()
- )
- .putTasksByTaskId(
- 3,
- DesktopTask.newBuilder()
- .setTaskId(3)
- .setDesktopTaskState(DesktopTaskState.MINIMIZED)
- .build()
- )
- .build()
+ val desktop1: Desktop =
+ Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_1)
+ .addAllZOrderedTasks(freeformTasksInZOrder1)
+ .putTasksByTaskId(
+ 1,
+ DesktopTask.newBuilder()
+ .setTaskId(1)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build(),
+ )
+ .putTasksByTaskId(
+ 3,
+ DesktopTask.newBuilder()
+ .setTaskId(3)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build(),
+ )
+ .build()
val freeformTasksInZOrder2 = listOf(4, 5)
- val desktop2: Desktop = Desktop.newBuilder()
- .setDesktopId(DESKTOP_ID_2)
- .addAllZOrderedTasks(freeformTasksInZOrder2)
- .putTasksByTaskId(
- 4,
- DesktopTask.newBuilder()
- .setTaskId(4)
- .setDesktopTaskState(DesktopTaskState.MINIMIZED)
- .build()
- )
- .putTasksByTaskId(
- 5,
- DesktopTask.newBuilder()
- .setTaskId(5)
- .setDesktopTaskState(DesktopTaskState.VISIBLE)
- .build()
- )
- .build()
+ val desktop2: Desktop =
+ Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_2)
+ .addAllZOrderedTasks(freeformTasksInZOrder2)
+ .putTasksByTaskId(
+ 4,
+ DesktopTask.newBuilder()
+ .setTaskId(4)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build(),
+ )
+ .putTasksByTaskId(
+ 5,
+ DesktopTask.newBuilder()
+ .setTaskId(5)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build(),
+ )
+ .build()
val freeformTasksInZOrder3 = listOf(7, 8)
- val desktop3: Desktop = Desktop.newBuilder()
- .setDesktopId(DESKTOP_ID_3)
- .addAllZOrderedTasks(freeformTasksInZOrder3)
- .putTasksByTaskId(
- 7,
- DesktopTask.newBuilder()
- .setTaskId(7)
- .setDesktopTaskState(DesktopTaskState.VISIBLE)
- .build()
- )
- .putTasksByTaskId(
- 8,
- DesktopTask.newBuilder()
- .setTaskId(8)
- .setDesktopTaskState(DesktopTaskState.MINIMIZED)
- .build()
- )
- .build()
- val desktopRepositoryState1: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
- .putDesktop(DESKTOP_ID_1, desktop1)
- .putDesktop(DESKTOP_ID_2, desktop2)
- .build()
- val desktopRepositoryState2: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
- .putDesktop(DESKTOP_ID_3, desktop3)
- .build()
+ val desktop3: Desktop =
+ Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_3)
+ .addAllZOrderedTasks(freeformTasksInZOrder3)
+ .putTasksByTaskId(
+ 7,
+ DesktopTask.newBuilder()
+ .setTaskId(7)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build(),
+ )
+ .putTasksByTaskId(
+ 8,
+ DesktopTask.newBuilder()
+ .setTaskId(8)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build(),
+ )
+ .build()
+ val desktopRepositoryState1: DesktopRepositoryState =
+ DesktopRepositoryState.newBuilder()
+ .putDesktop(DESKTOP_ID_1, desktop1)
+ .putDesktop(DESKTOP_ID_2, desktop2)
+ .build()
+ val desktopRepositoryState2: DesktopRepositoryState =
+ DesktopRepositoryState.newBuilder().putDesktop(DESKTOP_ID_3, desktop3).build()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index c6835b7bde55..22b45e8c63af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,7 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.launcher3.Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL;
+import static com.android.launcher3.Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
@@ -250,7 +250,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
t3.taskId, -1);
}
- @EnableFlags(FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
@Test
public void testGetRecentTasks_removesDesktopWallpaperActivity() {
RecentTaskInfo t1 = makeTaskInfo(1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index bb9703fce2e3..bca9c3fdda39 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -106,6 +106,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@Mock ShellExecutor mMainExecutor;
@Mock Handler mMainHandler;
+ @Mock ShellExecutor mBgExecutor;
@Mock DisplayController mDisplayController;
@Mock DisplayImeController mDisplayImeController;
@Mock DisplayInsetsController mDisplayInsetsController;
@@ -137,7 +138,8 @@ public class SplitScreenControllerTests extends ShellTestCase {
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
- mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler));
+ mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler,
+ mBgExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 1a2d60ddad3e..ada7b4aff37d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -36,6 +36,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitState;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.transition.Transitions;
@@ -78,14 +79,16 @@ public class SplitTestUtils {
StageTaskListener sideStage, DisplayController displayController,
DisplayImeController imeController, DisplayInsetsController insetsController,
SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
- ShellExecutor mainExecutor, Handler mainHandler,
+ ShellExecutor mainExecutor, Handler mainHandler, ShellExecutor bgExecutor,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+ Optional<DesktopTasksController> desktopTasksController) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
- launchAdjacentController, windowDecorViewModel, splitState);
+ transitions, transactionPool, mainExecutor, mainHandler, bgExecutor,
+ recentTasks, launchAdjacentController, windowDecorViewModel, splitState,
+ desktopTasksController);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index de77837cb0e5..ffa8b6089660 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -111,6 +111,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private WindowDecorViewModel mWindowDecorViewModel;
@Mock private SplitState mSplitState;
@Mock private ShellExecutor mMainExecutor;
+ @Mock private ShellExecutor mBgExecutor;
@Mock private Handler mMainHandler;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
@@ -136,17 +137,19 @@ public class SplitTransitionTests extends ShellTestCase {
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
- mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_MAIN));
+ mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
+ STAGE_TYPE_MAIN));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
- mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_SIDE));
+ mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
+ STAGE_TYPE_SIDE));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
- mLaunchAdjacentController, Optional.empty(), mSplitState);
+ mTransactionPool, mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
+ mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty());
mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
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 7afcce1243e3..9d1df864764f 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
@@ -119,6 +119,8 @@ public class StageCoordinatorTests extends ShellTestCase {
private DefaultMixedHandler mDefaultMixedHandler;
@Mock
private SplitState mSplitState;
+ @Mock
+ private ShellExecutor mBgExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -141,8 +143,9 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
- mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
- Optional.empty(), mSplitState));
+ mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
+ mLaunchAdjacentController, Optional.empty(), mSplitState,
+ Optional.empty()));
mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
new file mode 100644
index 000000000000..62b830dfd691
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.splitscreen
+
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.Flags.enableFlexibleSplit
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33
+import com.android.wm.shell.splitscreen.StageTaskListener.StageListenerCallbacks
+import com.android.wm.shell.windowdecor.WindowDecorViewModel
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import java.util.Optional
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StageOrderOperatorTests : ShellTestCase() {
+
+ @Mock
+ lateinit var mMainExecutor: ShellExecutor
+ @Mock
+ lateinit var mBgExecutor: ShellExecutor
+ @Mock
+ lateinit var mTaskOrganizer: ShellTaskOrganizer
+ @Mock
+ lateinit var mSyncQueue: SyncTransactionQueue
+ @Mock
+ lateinit var stageListenerCallbacks: StageListenerCallbacks
+ @Mock
+ lateinit var iconProvider: IconProvider
+ @Mock
+ lateinit var windowDecorViewModel: Optional<WindowDecorViewModel>
+
+ lateinit var stageOrderOperator: StageOrderOperator
+
+ @Before
+ fun setup() {
+ stageOrderOperator = StageOrderOperator(
+ context,
+ mTaskOrganizer,
+ DEFAULT_DISPLAY,
+ stageListenerCallbacks,
+ mSyncQueue,
+ iconProvider,
+ mMainExecutor,
+ mBgExecutor,
+ windowDecorViewModel,
+ )
+ assert(stageOrderOperator.activeStages.size == 0)
+ }
+
+ @Test
+ fun activeStages_2_2app_50_50_split() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_50_50)
+ assert(stageOrderOperator.activeStages.size == 2)
+ }
+
+ @Test
+ fun activeStages_2_2app_33_66_split() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_33_66)
+ assert(stageOrderOperator.activeStages.size == 2)
+ }
+
+ @Test
+ fun activeStages_2_2app_66_33_split() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ assert(stageOrderOperator.activeStages.size == 2)
+ }
+
+ @Test
+ fun activateSameCountStage_noOp() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ assert(stageOrderOperator.activeStages.size == 2)
+ }
+
+ @Test
+ fun deactivate_emptyActiveStages() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ stageOrderOperator.onExitingSplit()
+ assert(stageOrderOperator.activeStages.isEmpty())
+ }
+
+ @Test
+ fun swapDividerPos_twoApps() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ val stageIndex0: StageTaskListener = stageOrderOperator.activeStages[0]
+ val stageIndex1: StageTaskListener = stageOrderOperator.activeStages[1]
+
+ stageOrderOperator.onDoubleTappedDivider()
+ val newStageIndex0: StageTaskListener = stageOrderOperator.activeStages[0]
+ val newStageIndex1: StageTaskListener = stageOrderOperator.activeStages[1]
+
+ assert(stageIndex0 == newStageIndex1)
+ assert(stageIndex1 == newStageIndex0)
+ }
+
+ @Test
+ fun swapDividerPos_twiceNoOp_twoApps() {
+ assumeTrue(enableFlexibleSplit())
+
+ stageOrderOperator.onEnteringSplit(SNAP_TO_2_66_33)
+ val stageIndex0: StageTaskListener = stageOrderOperator.activeStages[0]
+ val stageIndex1: StageTaskListener = stageOrderOperator.activeStages[1]
+
+ stageOrderOperator.onDoubleTappedDivider()
+ stageOrderOperator.onDoubleTappedDivider()
+ val newStageIndex0: StageTaskListener = stageOrderOperator.activeStages[0]
+ val newStageIndex1: StageTaskListener = stageOrderOperator.activeStages[1]
+
+ assert(stageIndex0 == newStageIndex0)
+ assert(stageIndex1 == newStageIndex1)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index fe91440b106f..effc6a7daf62 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -43,6 +43,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -73,6 +74,10 @@ public final class StageTaskListenerTests extends ShellTestCase {
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
+ private ShellExecutor mMainExecutor;
+ @Mock
+ private ShellExecutor mBgExecutor;
+ @Mock
private IconProvider mIconProvider;
@Mock
private WindowDecorViewModel mWindowDecorViewModel;
@@ -95,6 +100,8 @@ public final class StageTaskListenerTests extends ShellTestCase {
mCallbacks,
mSyncQueue,
mIconProvider,
+ mMainExecutor,
+ mBgExecutor,
Optional.of(mWindowDecorViewModel),
STAGE_TYPE_UNDEFINED);
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
index e871711fd25e..cf6c3a5e03a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
@@ -63,6 +63,7 @@ class DesktopHeaderManageWindowsMenuTest : ShellTestCase() {
userRepositories = DesktopUserRepositories(
context = context,
shellInit = ShellInit(TestShellExecutor()),
+ shellController = mock(),
persistentRepository = mock(),
repositoryInitializer = mock(),
mainCoroutineScope = mock(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index a15b61122713..8b4cf6d1fabe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -125,6 +125,8 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
times(1)
).setAppHandleEducationTooltipCallbacks(openHandleMenuCallbackCaptor.capture(), any())
openHandleMenuCallbackCaptor.lastValue.invoke(task.taskId)
+ bgExecutor.flushAll()
+ testShellExecutor.flushAll()
verify(decor, times(1)).createHandleMenu(anyBoolean())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index a4e3af47edaa..aead0a7afb53 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -278,7 +278,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsNotCreatedForTopTranslucentActivities() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
}
@@ -388,7 +388,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
}
@Test
- fun testOnDecorMaximizedOrRestored_togglesTaskSize() {
+ fun testOnDecorMaximizedOrRestored_togglesTaskSize_maximize() {
val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -409,6 +409,52 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
}
@Test
+ fun testOnDecorMaximizedOrRestored_togglesTaskSize_maximizeFromMaximizedSize() {
+ val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
+ as ArgumentCaptor<Function0<Unit>>
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onMaxOrRestoreListenerCaptor = maxOrRestoreListenerCaptor
+ )
+ val movedMaximizedBounds = Rect(STABLE_BOUNDS)
+ movedMaximizedBounds.offset(10, 10)
+ decor.mTaskInfo.configuration.windowConfiguration.bounds.set(movedMaximizedBounds)
+
+ maxOrRestoreListenerCaptor.value.invoke()
+
+ verify(mockDesktopTasksController).toggleDesktopTaskSize(
+ decor.mTaskInfo,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.MAXIMIZE_MENU_TO_MAXIMIZE,
+ InputMethod.UNKNOWN_INPUT_METHOD
+ )
+ )
+ }
+
+ @Test
+ fun testOnDecorMaximizedOrRestored_togglesTaskSize_restore() {
+ val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
+ as ArgumentCaptor<Function0<Unit>>
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onMaxOrRestoreListenerCaptor = maxOrRestoreListenerCaptor
+ )
+ decor.mTaskInfo.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
+
+ maxOrRestoreListenerCaptor.value.invoke()
+
+ verify(mockDesktopTasksController).toggleDesktopTaskSize(
+ decor.mTaskInfo,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.MAXIMIZE_MENU_TO_RESTORE,
+ InputMethod.UNKNOWN_INPUT_METHOD
+ )
+ )
+ }
+
+ @Test
fun testOnDecorMaximizedOrRestored_closesMenus() {
val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
@@ -591,7 +637,8 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
verify(mockDesktopTasksController).moveTaskToDesktop(
eq(decor.mTaskInfo.taskId),
any(),
- eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
+ eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON),
+ anyOrNull()
)
}
@@ -780,6 +827,8 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
times(1)
).setAppHandleEducationTooltipCallbacks(openHandleMenuCallbackCaptor.capture(), any())
openHandleMenuCallbackCaptor.lastValue.invoke(task.taskId)
+ bgExecutor.flushAll()
+ testShellExecutor.flushAll()
verify(decor, times(1)).createHandleMenu(anyBoolean())
}
@@ -822,7 +871,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
)
verify(mockDesktopTasksController, times(1))
- .moveTaskToDesktop(any(), any(), any())
+ .moveTaskToDesktop(any(), any(), any(), anyOrNull())
}
@Test
@@ -966,11 +1015,11 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onCaptionButtonTouchListener = onTouchListenerCaptor
)
- whenever(mockTaskPositioner.onDragPositioningStart(any(), any(), any()))
+ whenever(mockTaskPositioner.onDragPositioningStart(any(), any(), any(), any()))
.thenReturn(INITIAL_BOUNDS)
- whenever(mockTaskPositioner.onDragPositioningMove(any(), any()))
+ whenever(mockTaskPositioner.onDragPositioningMove(any(), any(), any()))
.thenReturn(INITIAL_BOUNDS)
- whenever(mockTaskPositioner.onDragPositioningEnd(any(), any()))
+ whenever(mockTaskPositioner.onDragPositioningEnd(any(), any(), any()))
.thenReturn(INITIAL_BOUNDS)
val view = mock(View::class.java)
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 afd46078074c..b5e8cebc1277 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
@@ -74,6 +74,9 @@ import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.StubTransaction
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
import org.junit.Rule
@@ -89,6 +92,8 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.Optional
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
/**
* Utility class for tests of [DesktopModeWindowDecorViewModel]
@@ -131,6 +136,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockAssistContentRequester = mock<AssistContentRequester>()
protected val bgExecutor = TestShellExecutor()
protected val mockMultiInstanceHelper = mock<MultiInstanceHelper>()
+ private val mockWindowDecorViewHostSupplier =
+ mock<WindowDecorViewHostSupplier<WindowDecorViewHost>>()
protected val mockTasksLimiter = mock<DesktopTasksLimiter>()
protected val mockFreeformTaskTransitionStarter = mock<FreeformTaskTransitionStarter>()
protected val mockActivityOrientationChangeHandler =
@@ -145,7 +152,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockCaptionHandleRepository = mock<WindowDecorCaptionHandleRepository>()
protected val mockDesktopRepository: DesktopRepository = mock<DesktopRepository>()
protected val motionEvent = mock<MotionEvent>()
- val displayController = mock<DisplayController>()
val displayLayout = mock<DisplayLayout>()
protected lateinit var spyContext: TestableContext
private lateinit var desktopModeEventLogger: DesktopModeEventLogger
@@ -178,6 +184,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
testShellExecutor,
mockMainHandler,
mockMainChoreographer,
+ mock<MainCoroutineDispatcher>(),
+ mock<CoroutineScope>(),
bgExecutor,
shellInit,
mockShellCommandHandler,
@@ -193,6 +201,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopImmersiveController,
mockGenericLinksParser,
mockAssistContentRequester,
+ mockWindowDecorViewHostSupplier,
mockMultiInstanceHelper,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
@@ -209,7 +218,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockTaskPositionerFactory,
mockFocusTransitionObserver,
desktopModeEventLogger,
- mock<DesktopModeUiEventLogger>()
+ mock<DesktopModeUiEventLogger>(),
+ mock<WindowDecorTaskResourceLoader>()
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -250,7 +260,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
argumentCaptor<DesktopModeKeyguardChangeListener>()
verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture())
desktopModeOnKeyguardChangedListener = keyguardChangedCaptor.firstValue
- whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+ whenever(mockDisplayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
(i.arguments.first() as Rect).set(STABLE_BOUNDS)
}
@@ -281,6 +291,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
} else {
statusBars()
}
+ userId = context.userId
}
}
@@ -288,8 +299,9 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
val decoration = Mockito.mock(DesktopModeWindowDecoration::class.java)
whenever(
mockDesktopModeWindowDecorFactory.create(
- 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(), eq(task), 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 0bef41910146..855b3ddd99b5 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
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
@@ -39,7 +40,6 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
@@ -51,7 +51,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.kotlin.VerificationKt.times;
import android.app.ActivityManager;
import android.app.assist.AssistContent;
@@ -115,12 +114,18 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -157,6 +162,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/");
private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/");
private static final Uri TEST_URI3 = Uri.parse("https://slides.google.com/");
+ private static final Uri TEST_URI4 = Uri.parse("https://calendar.google.com/");
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -171,6 +177,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private Choreographer mMockChoreographer;
@Mock
+ private MainCoroutineDispatcher mMockMainCoroutineDispatcher;
+ @Mock
+ private CoroutineScope mMockBgCoroutineScope;
+ @Mock
private SyncTransactionQueue mMockSyncQueue;
@Mock
private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
@@ -187,6 +197,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private AttachedSurfaceControl mMockRootSurfaceControl;
@Mock
+ private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier;
+ @Mock
+ private WindowDecorViewHost mMockWindowDecorViewHost;
+ @Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
private TypedArray mMockRoundedCornersRadiusArray;
@@ -218,6 +232,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private DesktopModeEventLogger mDesktopModeEventLogger;
@Mock
private DesktopRepository mDesktopRepository;
+ @Mock
+ private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
@Captor
private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@Captor
@@ -228,6 +244,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
private final ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final ShellExecutor mMainExecutor = new TestShellExecutor();
private final AssistContent mAssistContent = new AssistContent();
private final Region mExclusionRegion = Region.obtain();
@@ -261,21 +278,24 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
final ActivityInfo activityInfo = createActivityInfo();
when(mMockPackageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo);
- final ResolveInfo resolveInfo = new ResolveInfo();
- resolveInfo.activityInfo = activityInfo;
- when(mMockPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
+ final ResolveInfo resolveInfo = createResolveInfo(false /* handleAllWebDataUri */);
+ when(mMockPackageManager.resolveActivityAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(resolveInfo);
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
- when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(), any(),
+ when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(),
anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
any(), anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
- when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
- any())).thenReturn(mMockAppHeaderViewHolder);
+ when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any()))
+ .thenReturn(mMockAppHeaderViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
+ when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
+ .thenReturn(mMockWindowDecorViewHost);
+ when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
}
@After
@@ -1052,61 +1072,70 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void relayout_fullscreenTask_appliesTransactionImmediately() {
+ public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ // Make the task fullscreen so that its decoration is an App Handle.
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop= */ false,
+ /* isStatusBarVisible= */ true,
+ /* isKeyguardVisibleAndOccluded= */ false,
+ /* inFullImmersiveMode= */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
- verify(mMockTransaction).apply();
- verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
+ // App Handles don't need to be rendered in sync with the task animation, per UX.
+ assertThat(relayoutParams.mAsyncViewHost).isTrue();
}
@Test
- @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
- public void relayout_freeformTask_appliesTransactionOnDraw() {
+ public void updateRelayoutParams_header_requestsSyncViewHostRendering() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ // Make the task freeform so that its decoration is an App Header.
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
- taskInfo.isResizeable = false;
-
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- verify(mMockTransaction, never()).apply();
- verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
- }
-
- @Test
- public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop= */ false,
+ /* isStatusBarVisible= */ true,
+ /* isKeyguardVisibleAndOccluded= */ false,
+ /* inFullImmersiveMode= */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
- verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
+ // App Headers must be rendered in sync with the task animation, so it cannot be delayed.
+ assertThat(relayoutParams.mAsyncViewHost).isFalse();
}
@Test
- public void relayout_fullscreenTask_postsViewHostCreation() {
+ public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
- runnableArgument.getValue().run();
- verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+ verify(mMockTransaction).apply();
+ verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
}
@Test
@Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
- public void relayout_freeformTask_createsViewHostImmediately() {
+ public void relayout_freeformTask_appliesTransactionOnDraw() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -1115,38 +1144,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
- verify(mMockHandler, never()).post(any());
- }
-
- @Test
- public void relayout_removesExistingHandlerCallback() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
-
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
- }
-
- @Test
- public void close_removesExistingHandlerCallback() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
-
- spyWindowDecor.close();
-
- verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+ verify(mMockTransaction, never()).apply();
+ verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
}
@Test
@@ -1336,11 +1335,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() {
+ public void capturedLink_CapturedLinkUsedIfValidAndWebUriUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
- TEST_URI3 /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* session transfer uri */, TEST_URI4 /* generic link */);
// Verify handle menu's browser link set as captured link
createHandleMenu(decor);
@@ -1349,72 +1348,45 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
- final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
-
- // Verify captured link is no longer valid by verifying link is not set as handle menu
- // browser link.
- createHandleMenu(decor);
- verifyHandleMenuCreated(null /* uri */);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
public void capturedLink_capturedLinkNotResetToSameLink() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+ null /* session transfer uri */, null /* generic link */);
+ final ArgumentCaptor<Function1<Intent, Unit>> openInBrowserCaptor =
+ ArgumentCaptor.forClass(Function1.class);
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
+ createHandleMenu(decor);
+ verify(mMockHandleMenu).show(any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ openInBrowserCaptor.capture(),
+ any(),
+ any(),
+ any(),
+ anyBoolean()
+ );
+ // Run runnable to set captured link to used
+ openInBrowserCaptor.getValue().invoke(new Intent(Intent.ACTION_MAIN, TEST_URI1));
// Relayout decor with same captured link
decor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Verify handle menu's browser link not set to captured link since link is expired
+ // Verify handle menu's browser link not set to captured link since link is already used
createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
- final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-
- // Create handle menu before link expires
- createHandleMenu(decor);
-
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
-
- // Verify handle menu's browser link is set to captured link since menu was opened before
- // captured link expired
- verifyHandleMenuCreated(TEST_URI1);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_capturedLinkExpiresAfterClick() {
+ public void capturedLink_capturedLinkSetToUsedAfterClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
+ null /* session transfer uri */, null /* generic link */);
final ArgumentCaptor<Function1<Intent, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
@@ -1447,7 +1419,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
+ null /* session transfer uri */, null /* generic link */);
final ArgumentCaptor<Function1<Intent, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
createHandleMenu(decor);
@@ -1473,11 +1445,23 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void webUriLink_webUriLinkUsedWhenCapturedLinkUnavailable() {
+ public void webUriLink_webUriLinkUsedWhenWhenAvailable() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* session transfer uri */, TEST_URI4 /* generic link */);
+ // Verify handle menu's browser link set as web uri link when captured link is unavailable
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI3);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void webUriLink_webUriLinkUsedWhenSessionTransferUriUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, null /* captured link */, TEST_URI2 /* web uri */,
- TEST_URI3 /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ null /* session transfer uri */, TEST_URI4 /* generic link */);
// Verify handle menu's browser link set as web uri link when captured link is unavailable
createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI2);
@@ -1489,12 +1473,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, null /* captured link */, null /* web uri */,
- TEST_URI3 /* generic link */);
+ null /* session transfer uri */, TEST_URI4 /* generic link */);
// Verify handle menu's browser link set as generic link when captured link and web uri link
// are unavailable
createHandleMenu(decor);
- verifyHandleMenuCreated(TEST_URI3);
+ verifyHandleMenuCreated(TEST_URI4);
}
@Test
@@ -1678,7 +1662,26 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void browserApp_webUriUsedForBrowserApp() {
+ public void browserApp_transferSessionUriUsedForBrowserAppWhenAvailable() {
+ // Make {@link AppToWebUtils#isBrowserApp} return true
+ ResolveInfo browserResolveInfo = createResolveInfo(true /* handleAllWebUriData */);
+ when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(browserResolveInfo));
+
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ null /* transfer session uri */, TEST_URI4 /* generic link */);
+
+ // Verify web uri used for browser applications
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI2);
+ }
+
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void browserApp_webUriUsedForBrowserAppWhenTransferSessionUriUnavailable() {
// Make {@link AppToWebUtils#isBrowserApp} return true
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.handleAllWebDataURI = true;
@@ -1689,16 +1692,16 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
- TEST_URI3 /* generic link */);
+ TEST_URI3 /* transfer session uri */, TEST_URI4 /* generic link */);
// Verify web uri used for browser applications
createHandleMenu(decor);
- verifyHandleMenuCreated(TEST_URI2);
+ verifyHandleMenuCreated(TEST_URI3);
}
private void verifyHandleMenuCreated(@Nullable Uri uri) {
- verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
+ verify(mMockHandleMenuFactory).create(any(), any(), any(), any(), any(), anyInt(),
any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
anyBoolean(), argThat(intent ->
(uri == null && intent == null) || intent.getData().equals(uri)),
@@ -1733,10 +1736,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink,
- @Nullable Uri webUri, @Nullable Uri genericLink) {
+ @Nullable Uri webUri, @Nullable Uri sessionTransferUri, @Nullable Uri genericLink) {
taskInfo.capturedLink = capturedLink;
taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
mAssistContent.setWebUri(webUri);
+ mAssistContent.getExtras().putObject(EXTRA_SESSION_TRANSFER_WEB_URI, sessionTransferUri);
final String genericLinkString = genericLink == null ? null : genericLink.toString();
doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
// Relayout to set captured link
@@ -1765,14 +1769,16 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
MaximizeMenuFactory maximizeMenuFactory,
boolean relayout) {
final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
- mContext, mMockDisplayController, mMockSplitScreenController,
- mMockDesktopUserRepositories, mMockShellTaskOrganizer, taskInfo,
- mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer, mMockSyncQueue,
- mMockAppHeaderViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
- mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
- mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
+ mContext, mMockDisplayController, mMockTaskResourceLoader,
+ mMockSplitScreenController, mMockDesktopUserRepositories, mMockShellTaskOrganizer,
+ taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
+ mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
+ mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
+ mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
+ mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
+ WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
- maximizeMenuFactory, mMockHandleMenuFactory,
+ mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
@@ -1785,6 +1791,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
return windowDecor;
}
+ private ResolveInfo createResolveInfo(boolean handleAllWebDataURI) {
+ final ResolveInfo info = new ResolveInfo();
+ info.handleAllWebDataURI = handleAllWebDataURI;
+ info.activityInfo = createActivityInfo();
+ return info;
+ }
+
private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
new ActivityManager.TaskDescription.Builder();
@@ -1813,6 +1826,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
applicationInfo.packageName = "DesktopModeWindowDecorationTestPackage";
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.applicationInfo = applicationInfo;
+ activityInfo.packageName = "DesktopModeWindowDecorationTestPackage";
activityInfo.name = "DesktopModeWindowDecorationTest";
return activityInfo;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
index ce17c1df50bc..3c3d6b6bb258 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
@@ -68,9 +68,9 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
configuration.windowConfiguration.setBounds(PORTRAIT_BOUNDS)
}
doReturn(PORTRAIT_BOUNDS).`when`(mockTaskPositioner).onDragPositioningStart(
- any(), any(), any())
- doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningMove(any(), any())
- doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningEnd(any(), any())
+ any(), any(), any(), any())
+ doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningMove(any(), any(), any())
+ doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningEnd(any(), any(), any())
decoratedTaskPositioner = spy(
FixedAspectRatioTaskPositionerDecorator(
mockDesktopWindowDecoration, mockTaskPositioner)
@@ -87,7 +87,8 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
isResizeable = testCase.isResizeable
}
- decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalY)
+ decoratedTaskPositioner.onDragPositioningStart(
+ testCase.ctrlType, DISPLAY_ID, originalX, originalY)
val capturedValues = getLatestOnStartArguments()
assertThat(capturedValues.ctrlType).isEqualTo(testCase.ctrlType)
@@ -102,7 +103,8 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val originalX = 0f
val originalY = 0f
- decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalY)
+ decoratedTaskPositioner.onDragPositioningStart(
+ testCase.ctrlType, DISPLAY_ID, originalX, originalY)
val capturedValues = getLatestOnStartArguments()
assertThat(capturedValues.ctrlType).isEqualTo(testCase.ctrlType)
@@ -119,7 +121,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
val adjustedCtrlType = testCase.ctrlType + testCase.additionalEdgeCtrlType
val capturedValues = getLatestOnStartArguments()
@@ -134,13 +136,14 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
) {
val originalX = 0f
val originalY = 0f
- decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalX)
+ decoratedTaskPositioner.onDragPositioningStart(
+ testCase.ctrlType, DISPLAY_ID, originalX, originalX)
mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
isResizeable = testCase.isResizeable
}
decoratedTaskPositioner.onDragPositioningMove(
- originalX + SMALL_DELTA, originalY + SMALL_DELTA)
+ DISPLAY_ID, originalX + SMALL_DELTA, originalY + SMALL_DELTA)
val capturedValues = getLatestOnMoveArguments()
assertThat(capturedValues.x).isEqualTo(originalX + SMALL_DELTA)
@@ -156,13 +159,14 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
val updatedBounds = decoratedTaskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
startingPoint.x + testCase.dragDelta.x,
startingPoint.y + testCase.dragDelta.y)
- verify(mockTaskPositioner, never()).onDragPositioningMove(any(), any())
+ verify(mockTaskPositioner, never()).onDragPositioningMove(any(), any(), any())
assertThat(updatedBounds).isEqualTo(startingBounds)
}
@@ -176,10 +180,12 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
decoratedTaskPositioner.onDragPositioningMove(
- startingPoint.x + testCase.dragDelta.x, startingPoint.y + testCase.dragDelta.y)
+ DISPLAY_ID,
+ startingPoint.x + testCase.dragDelta.x,
+ startingPoint.y + testCase.dragDelta.y)
val adjustedDragDelta = calculateAdjustedDelta(
testCase.ctrlType, testCase.dragDelta, orientation)
@@ -202,9 +208,10 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
decoratedTaskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
startingPoint.x + testCase.dragDelta.x,
startingPoint.y + testCase.dragDelta.y)
@@ -227,13 +234,14 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
) {
val originalX = 0f
val originalY = 0f
- decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalX)
+ decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, DISPLAY_ID,
+ originalX, originalX)
mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
isResizeable = testCase.isResizeable
}
decoratedTaskPositioner.onDragPositioningEnd(
- originalX + SMALL_DELTA, originalY + SMALL_DELTA)
+ DISPLAY_ID, originalX + SMALL_DELTA, originalY + SMALL_DELTA)
val capturedValues = getLatestOnEndArguments()
assertThat(capturedValues.x).isEqualTo(originalX + SMALL_DELTA)
@@ -249,9 +257,10 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
decoratedTaskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
startingPoint.x + testCase.dragDelta.x,
startingPoint.y + testCase.dragDelta.y)
@@ -269,10 +278,12 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
decoratedTaskPositioner.onDragPositioningEnd(
- startingPoint.x + testCase.dragDelta.x, startingPoint.y + testCase.dragDelta.y)
+ DISPLAY_ID,
+ startingPoint.x + testCase.dragDelta.x,
+ startingPoint.y + testCase.dragDelta.y)
val adjustedDragDelta = calculateAdjustedDelta(
testCase.ctrlType, testCase.dragDelta, orientation)
@@ -295,9 +306,10 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
decoratedTaskPositioner.onDragPositioningStart(
- testCase.ctrlType, startingPoint.x, startingPoint.y)
+ testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
decoratedTaskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
startingPoint.x + testCase.dragDelta.x,
startingPoint.y + testCase.dragDelta.y)
@@ -322,7 +334,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
val captorCtrlType = argumentCaptor<Int>()
val captorCoordinates = argumentCaptor<Float>()
verify(mockTaskPositioner).onDragPositioningStart(
- captorCtrlType.capture(), captorCoordinates.capture(), captorCoordinates.capture())
+ captorCtrlType.capture(), any(), captorCoordinates.capture(), captorCoordinates.capture())
return CtrlCoordinateCapture(captorCtrlType.firstValue, captorCoordinates.firstValue,
captorCoordinates.secondValue)
@@ -335,7 +347,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
private fun getLatestOnMoveArguments(): PointF {
val captorCoordinates = argumentCaptor<Float>()
verify(mockTaskPositioner).onDragPositioningMove(
- captorCoordinates.capture(), captorCoordinates.capture())
+ any(), captorCoordinates.capture(), captorCoordinates.capture())
return PointF(captorCoordinates.firstValue, captorCoordinates.secondValue)
}
@@ -347,7 +359,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
private fun getLatestOnEndArguments(): PointF {
val captorCoordinates = argumentCaptor<Float>()
verify(mockTaskPositioner).onDragPositioningEnd(
- captorCoordinates.capture(), captorCoordinates.capture())
+ any(), captorCoordinates.capture(), captorCoordinates.capture())
return PointF(captorCoordinates.firstValue, captorCoordinates.secondValue)
}
@@ -358,7 +370,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
private fun getAndMockBounds(orientation: Orientation): Rect {
val mockBounds = if (orientation.isPortrait) PORTRAIT_BOUNDS else LANDSCAPE_BOUNDS
doReturn(mockBounds).`when`(mockTaskPositioner).onDragPositioningStart(
- any(), any(), any())
+ any(), any(), any(), any())
doReturn(mockBounds).`when`(decoratedTaskPositioner).getBounds(any())
return mockBounds
}
@@ -458,6 +470,7 @@ class FixedAspectRatioTaskPositionerDecoratorTests : ShellTestCase(){
private val STARTING_ASPECT_RATIO = PORTRAIT_BOUNDS.height() / PORTRAIT_BOUNDS.width()
private const val LARGE_DELTA = 50f
private const val SMALL_DELTA = 30f
+ private const val DISPLAY_ID = 1
enum class Orientation(
val isPortrait: Boolean
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 3b80cb4936b9..cec52518edd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -150,11 +150,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_notMove_skipsTransitionOnEnd() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -171,11 +173,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_noEffectiveMove_skipsTransitionOnMoveAndEnd() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -188,6 +192,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
})
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -204,11 +209,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_hasEffectiveMove_issuesTransitionOnMoveAndEnd() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat()
)
@@ -224,6 +231,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -242,6 +250,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_move_skipsDragResizingFlag() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // Move
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -250,11 +259,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.left.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -276,6 +286,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setsDragResizingFlag() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -284,11 +295,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -310,6 +322,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -318,11 +331,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 5
val newY = STARTING_BOUNDS.top.toFloat() + 95
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -340,6 +354,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -348,11 +363,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 95
val newY = STARTING_BOUNDS.top.toFloat() + 5
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -370,6 +386,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -378,11 +395,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 5
val newY = STARTING_BOUNDS.top.toFloat() + 105
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -400,6 +418,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -408,11 +427,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 105
val newY = STARTING_BOUNDS.top.toFloat() + 5
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -430,6 +450,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -438,11 +459,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 80
val newY = STARTING_BOUNDS.top.toFloat() + 80
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -456,6 +478,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -464,11 +487,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 95
val newY = STARTING_BOUNDS.top.toFloat() + 95
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -484,6 +508,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -492,11 +517,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 97
val newY = STARTING_BOUNDS.top.toFloat() + 97
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -510,6 +536,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_useMinWidthWhenValid() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -518,11 +545,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.right.toFloat() - 93
val newY = STARTING_BOUNDS.top.toFloat() + 93
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -535,6 +563,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_toDisallowedBounds_freezesAtLimit() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.bottom.toFloat()
)
@@ -546,6 +575,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
STARTING_BOUNDS.right + 10,
STARTING_BOUNDS.bottom + 10)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newBounds.right.toFloat(),
newBounds.bottom.toFloat()
)
@@ -559,11 +589,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
DISALLOWED_RESIZE_AREA.top
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newBounds2.right.toFloat(),
newBounds2.bottom.toFloat()
)
- taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newBounds2.right.toFloat(),
+ newBounds2.bottom.toFloat())
// The first resize falls in the allowed area, verify there's a change for it.
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
@@ -629,6 +661,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
mockWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -645,6 +678,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
mockWindowDecoration.mHasGlobalFocus = true
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -661,6 +695,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
mockWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -729,11 +764,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() - 20,
STARTING_BOUNDS.top.toFloat() - 20
)
@@ -742,6 +779,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
assertTrue(taskPositioner.isResizingOrAnimating)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -785,15 +823,18 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
) {
taskPositioner.onDragPositioningStart(
ctrlType,
+ DISPLAY_ID,
startX,
startY
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
endX,
endY
)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
endX,
endY
)
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 6babf817686a..cbfb57edc72d 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
@@ -24,6 +24,7 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Point
import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
@@ -49,6 +50,13 @@ import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UND
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -68,6 +76,7 @@ import org.mockito.kotlin.whenever
* Build/Install/Run:
* atest WMShellUnitTests:HandleMenuTest
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@@ -81,14 +90,6 @@ class HandleMenuTest : ShellTestCase() {
@Mock
private lateinit var mockWindowManager: WindowManager
@Mock
- private lateinit var onClickListener: View.OnClickListener
- @Mock
- private lateinit var onTouchListener: View.OnTouchListener
- @Mock
- private lateinit var appIcon: Bitmap
- @Mock
- private lateinit var appName: CharSequence
- @Mock
private lateinit var displayController: DisplayController
@Mock
private lateinit var splitScreenController: SplitScreenController
@@ -96,6 +97,10 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var displayLayout: DisplayLayout
@Mock
private lateinit var mockSurfaceControlViewHost: SurfaceControlViewHost
+ @Mock
+ private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
+ @Mock
+ private lateinit var mockAppIcon: Bitmap
private lateinit var handleMenu: HandleMenu
@@ -136,7 +141,7 @@ class HandleMenuTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
- fun testFullscreenMenuUsesSystemViewContainer() {
+ fun testFullscreenMenuUsesSystemViewContainer() = runTest {
createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED)
val handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -148,7 +153,7 @@ class HandleMenuTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
- fun testFreeformMenu_usesViewHostViewContainer() {
+ fun testFreeformMenu_usesViewHostViewContainer() = runTest {
createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED)
handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalViewHostViewContainer)
@@ -159,7 +164,7 @@ class HandleMenuTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
- fun testSplitLeftMenu_usesSystemViewContainer() {
+ fun testSplitLeftMenu_usesSystemViewContainer() = runTest {
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT)
handleMenu = createAndShowHandleMenu(SPLIT_POSITION_TOP_OR_LEFT)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -174,7 +179,7 @@ class HandleMenuTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
- fun testSplitRightMenu_usesSystemViewContainer() {
+ fun testSplitRightMenu_usesSystemViewContainer() = runTest {
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT)
handleMenu = createAndShowHandleMenu(SPLIT_POSITION_BOTTOM_OR_RIGHT)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -188,7 +193,7 @@ class HandleMenuTest : ShellTestCase() {
}
@Test
- fun testCreate_forceShowSystemBars_usesSystemViewContainer() {
+ fun testCreate_forceShowSystemBars_usesSystemViewContainer() = runTest {
createTaskInfo(WINDOWING_MODE_FREEFORM)
handleMenu = createAndShowHandleMenu(forceShowSystemBars = true)
@@ -198,7 +203,7 @@ class HandleMenuTest : ShellTestCase() {
}
@Test
- fun testCreate_forceShowSystemBars() {
+ fun testCreate_forceShowSystemBars() = runTest {
createTaskInfo(WINDOWING_MODE_FREEFORM)
handleMenu = createAndShowHandleMenu(forceShowSystemBars = true)
@@ -208,6 +213,18 @@ class HandleMenuTest : ShellTestCase() {
assertTrue((types and systemBars()) != 0)
}
+ @Test
+ fun testCreate_loadsAppInfoInBackground() = runTest {
+ createTaskInfo(WINDOWING_MODE_FREEFORM)
+
+ handleMenu = createAndShowHandleMenu()
+ advanceUntilIdle()
+
+ assertThat(handleMenu.handleMenuView!!.appNameView.text).isEqualTo(APP_NAME)
+ val drawable = handleMenu.handleMenuView!!.appIconView.drawable as BitmapDrawable
+ assertThat(drawable.bitmap).isEqualTo(mockAppIcon)
+ }
+
private fun createTaskInfo(windowingMode: Int, splitPosition: Int? = null) {
val taskDescriptionBuilder = ActivityManager.TaskDescription.Builder()
.setBackgroundColor(Color.YELLOW)
@@ -238,9 +255,13 @@ class HandleMenuTest : ShellTestCase() {
(it.arguments[1] as Rect).set(SPLIT_RIGHT_BOUNDS)
}
}
+ whenever(mockTaskResourceLoader.getName(mockDesktopWindowDecoration.mTaskInfo))
+ .thenReturn(APP_NAME)
+ whenever(mockTaskResourceLoader.getHeaderIcon(mockDesktopWindowDecoration.mTaskInfo))
+ .thenReturn(mockAppIcon)
}
- private fun createAndShowHandleMenu(
+ private fun TestScope.createAndShowHandleMenu(
splitPosition: Int? = null,
forceShowSystemBars: Boolean = false
): HandleMenu {
@@ -257,17 +278,27 @@ class HandleMenuTest : ShellTestCase() {
if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
(SPLIT_LEFT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
} else {
- (SPLIT_RIGHT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
+ SPLIT_LEFT_BOUNDS.width() + (SPLIT_RIGHT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
}
}
else -> error("Invalid windowing mode")
}
- val handleMenu = HandleMenu(mockDesktopWindowDecoration,
+ val handleMenu = HandleMenu(
+ StandardTestDispatcher(testScheduler),
+ this,
+ mockDesktopWindowDecoration,
WindowManagerWrapper(mockWindowManager),
- layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
- shouldShowNewWindowButton = true, shouldShowManageWindowsButton = false,
- shouldShowChangeAspectRatioButton = false, shouldShowDesktopModeButton = true,
- isBrowserApp = false, null /* openInAppOrBrowserIntent */, captionWidth = HANDLE_WIDTH,
+ mockTaskResourceLoader,
+ layoutId,
+ splitScreenController,
+ shouldShowWindowingPill = true,
+ shouldShowNewWindowButton = true,
+ shouldShowManageWindowsButton = false,
+ shouldShowChangeAspectRatioButton = false,
+ shouldShowDesktopModeButton = true,
+ isBrowserApp = false,
+ null /* openInAppOrBrowserIntent */,
+ captionWidth = HANDLE_WIDTH,
captionHeight = 50,
captionX = captionX,
captionY = 0,
@@ -300,5 +331,6 @@ class HandleMenuTest : ShellTestCase() {
private const val MENU_PILL_ELEVATION = 2
private const val MENU_PILL_SPACING_MARGIN = 4
private const val HANDLE_WIDTH = 80
+ private const val APP_NAME = "Test App"
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index e0d16aab1e07..fa3d3e4016e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -17,6 +17,7 @@ package com.android.wm.shell.windowdecor
import android.graphics.Bitmap
import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -29,6 +30,13 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,6 +62,7 @@ import org.mockito.kotlin.whenever
* Build/Install/Run:
* atest WMShellUnitTests:ResizeVeilTest
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@@ -85,6 +94,8 @@ class ResizeVeilTest : ShellTestCase() {
private lateinit var mockIconSurface: SurfaceControl
@Mock
private lateinit var mockTransaction: SurfaceControl.Transaction
+ @Mock
+ private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
private val taskInfo = TestRunningTaskInfoBuilder().build()
@@ -115,7 +126,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun init_displayAvailable_viewHostCreated() {
+ fun init_displayAvailable_viewHostCreated() = runTest {
createResizeVeil(withDisplayAvailable = true)
verify(mockSurfaceControlViewHostFactory)
@@ -123,7 +134,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun init_displayUnavailable_viewHostNotCreatedUntilDisplayAppears() {
+ fun init_displayUnavailable_viewHostNotCreatedUntilDisplayAppears() = runTest {
createResizeVeil(withDisplayAvailable = false)
verify(mockSurfaceControlViewHostFactory, never())
@@ -140,14 +151,14 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun dispose_removesDisplayWindowListener() {
+ fun dispose_removesDisplayWindowListener() = runTest {
createResizeVeil().dispose()
verify(mockDisplayController).removeDisplayWindowListener(any())
}
@Test
- fun showVeil() {
+ fun showVeil() = runTest {
val veil = createResizeVeil()
veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -159,7 +170,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun showVeil_displayUnavailable_doesNotShow() {
+ fun showVeil_displayUnavailable_doesNotShow() = runTest {
val veil = createResizeVeil(withDisplayAvailable = false)
veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -171,7 +182,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun showVeil_alreadyVisible_doesNotShowAgain() {
+ fun showVeil_alreadyVisible_doesNotShowAgain() = runTest {
val veil = createResizeVeil()
veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -184,7 +195,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun showVeil_reparentsVeilToNewParent() {
+ fun showVeil_reparentsVeilToNewParent() = runTest {
val veil = createResizeVeil(parent = mock())
val newParent = mock<SurfaceControl>()
@@ -200,7 +211,7 @@ class ResizeVeilTest : ShellTestCase() {
}
@Test
- fun hideVeil_alreadyHidden_doesNothing() {
+ fun hideVeil_alreadyHidden_doesNothing() = runTest {
val veil = createResizeVeil()
veil.hideVeil()
@@ -208,16 +219,41 @@ class ResizeVeilTest : ShellTestCase() {
verifyZeroInteractions(mockTransaction)
}
- private fun createResizeVeil(
+ @Test
+ fun showVeil_loadsIconInBackground() = runTest {
+ val veil = createResizeVeil()
+ veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
+
+ advanceUntilIdle()
+
+ verify(mockTaskResourceLoader).getVeilIcon(taskInfo)
+ assertThat((veil.iconView.drawable as BitmapDrawable).bitmap).isEqualTo(mockAppIcon)
+ }
+
+ @Test
+ fun dispose_iconLoading_cancelsJob() = runTest {
+ val veil = createResizeVeil()
+ veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
+
+ veil.dispose()
+ advanceUntilIdle()
+
+ assertThat(veil.iconView.drawable).isNull()
+ }
+
+ private fun TestScope.createResizeVeil(
withDisplayAvailable: Boolean = true,
parent: SurfaceControl = mock()
): ResizeVeil {
whenever(mockDisplayController.getDisplay(taskInfo.displayId))
.thenReturn(if (withDisplayAvailable) mockDisplay else null)
+ whenever(mockTaskResourceLoader.getVeilIcon(taskInfo)).thenReturn(mockAppIcon)
return ResizeVeil(
context,
mockDisplayController,
- mockAppIcon,
+ mockTaskResourceLoader,
+ StandardTestDispatcher(testScheduler),
+ this,
parent,
{ mockTransaction },
mockSurfaceControlBuilderFactory,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index e7df8643ba66..eb8c0dd365a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -168,12 +168,14 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_noMove_doesNotShowResizeVeil() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
verify(mockDesktopWindowDecoration, never()).showResizeVeil(STARTING_BOUNDS)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -191,11 +193,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_movesTask_doesNotShowResizeVeil() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 60,
STARTING_BOUNDS.top.toFloat() + 100
)
@@ -208,6 +212,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
eq(rectAfterMove.top.toFloat()))
val endBounds = taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 70,
STARTING_BOUNDS.top.toFloat() + 20
)
@@ -226,11 +231,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_resize_boundsUpdateOnEnd() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -248,6 +255,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
})
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.right.toFloat() + 20,
STARTING_BOUNDS.top.toFloat() + 20
)
@@ -266,17 +274,20 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
@Test
fun testDragResize_noEffectiveMove_skipsTransactionOnEnd() = runOnUiThread {
taskPositioner.onDragPositioningStart(
+ DISPLAY_ID,
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -300,6 +311,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -307,11 +319,12 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
val newX = STARTING_BOUNDS.left.toFloat() + 5
val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
newX,
newY
)
- taskPositioner.onDragPositioningEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -326,6 +339,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -342,6 +356,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = true
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -358,6 +373,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -422,11 +438,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat() - 20,
STARTING_BOUNDS.top.toFloat() - 20
)
@@ -436,6 +454,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
@@ -501,15 +520,18 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
) {
taskPositioner.onDragPositioningStart(
ctrlType,
+ DISPLAY_ID,
startX,
startY
)
taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
endX,
endY
)
taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
endX,
endY
)
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 04b2be0b1a25..d9693460008f 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,6 +61,7 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
@@ -88,6 +89,8 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.tests.R;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import org.junit.Before;
import org.junit.Rule;
@@ -130,6 +133,10 @@ public class WindowDecorationTests extends ShellTestCase {
@Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
+ private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier;
+ @Mock
+ private WindowDecorViewHost mMockWindowDecorViewHost;
+ @Mock
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
private AttachedSurfaceControl mMockRootSurfaceControl;
@@ -143,6 +150,8 @@ public class WindowDecorationTests extends ShellTestCase {
private SurfaceControl mMockTaskSurface;
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
+ @Mock
+ private Handler mMockHandler;
private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
new ArrayList<>();
@@ -177,6 +186,10 @@ public class WindowDecorationTests extends ShellTestCase {
// Add status bar inset so that WindowDecoration does not think task is in immersive mode
mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
+
+ when(mMockWindowDecorViewHostSupplier.acquire(any(), any()))
+ .thenReturn(mMockWindowDecorViewHost);
+ when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
}
@Test
@@ -233,10 +246,6 @@ public class WindowDecorationTests extends ShellTestCase {
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setDisplayId(Display.DEFAULT_DISPLAY)
@@ -257,18 +266,19 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true);
verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100);
- verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
- verify(captionContainerSurfaceBuilder).setContainerLayer();
+ final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
+ verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
- verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
-
- verify(mMockSurfaceControlViewHost)
- .setView(same(mMockView),
- argThat(lp -> lp.height == 64
- && lp.width == 300
- && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
+ verify(mMockWindowDecorViewHost).updateView(
+ same(mMockView),
+ argThat(lp -> lp.height == 64
+ && lp.width == 300
+ && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0),
+ eq(taskInfo.configuration),
+ any(),
+ eq(null) /* onDrawTransaction */);
verify(mMockView).setTaskFocusState(true);
verify(mMockWindowContainerTransaction).addInsetsSource(
eq(taskInfo.token),
@@ -297,10 +307,6 @@ public class WindowDecorationTests extends ShellTestCase {
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -323,7 +329,7 @@ public class WindowDecorationTests extends ShellTestCase {
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(mMockSurfaceControlViewHost, never()).release();
+ verify(mMockWindowDecorViewHost, never()).release(any());
verify(t, never()).apply();
verify(mMockWindowContainerTransaction, never())
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
@@ -333,9 +339,8 @@ public class WindowDecorationTests extends ShellTestCase {
taskInfo.isVisible = false;
windowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
- final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
- releaseOrder.verify(mMockSurfaceControlViewHost).release();
- releaseOrder.verify(t2).remove(captionContainerSurface);
+ final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier);
+ releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2);
releaseOrder.verify(t2).remove(decorContainerSurface);
releaseOrder.verify(t2).apply();
// Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
@@ -383,8 +388,8 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
- verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any());
- verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
+ verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay));
+ verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any(), any());
}
@Test
@@ -397,10 +402,6 @@ public class WindowDecorationTests extends ShellTestCase {
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -436,8 +437,7 @@ public class WindowDecorationTests extends ShellTestCase {
windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
- verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
- .create(any(), eq(defaultDisplay), any());
+ verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
}
@Test
@@ -450,10 +450,6 @@ public class WindowDecorationTests extends ShellTestCase {
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -472,8 +468,8 @@ public class WindowDecorationTests extends ShellTestCase {
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
- verify(captionContainerSurfaceBuilder).setContainerLayer();
+ final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
+ verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
// Width of the captionContainerSurface should match the width of TASK_BOUNDS
verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
@@ -489,10 +485,6 @@ public class WindowDecorationTests extends ShellTestCase {
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -509,10 +501,11 @@ public class WindowDecorationTests extends ShellTestCase {
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
- windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */,
- true /* hasGlobalFocus */, Region.obtain());
+ mRelayoutParams.mApplyStartTransactionOnDraw = true;
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain());
- verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), any(),
+ eq(mMockSurfaceControlStartT));
}
@Test
@@ -901,37 +894,69 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
+ public void relayout_applyTransactionOnDrawIsTrue_updatesViewWithDrawTransaction() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = true;
mRelayoutResult.mRootView = mMockView;
- windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
-
- verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain());
+
+ verify(mMockWindowDecorViewHost)
+ .updateView(
+ eq(mRelayoutResult.mRootView),
+ any(),
+ eq(windowDecor.mTaskInfo.configuration),
+ any(),
+ eq(mMockSurfaceControlStartT));
+ windowDecor.close();
}
@Test
- public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
+ public void relayout_applyTransactionOnDrawIsTrue_asyncViewHostRendering_throwsException() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = true;
+ mRelayoutParams.mAsyncViewHost = true;
mRelayoutResult.mRootView = mMockView;
assertThrows(IllegalArgumentException.class,
- () -> windowDecor.updateViewHost(
- mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
+ () -> windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain()));
+ windowDecor.close();
}
@Test
- public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
+ public void relayout_asyncViewHostRendering() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = false;
+ mRelayoutParams.mAsyncViewHost = true;
mRelayoutResult.mRootView = mMockView;
- windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
+ windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain());
+
+ verify(mMockWindowDecorViewHost)
+ .updateViewAsync(eq(mRelayoutResult.mRootView), any(),
+ eq(windowDecor.mTaskInfo.configuration), any());
+ windowDecor.close();
}
@Test
@@ -1015,7 +1040,8 @@ public class WindowDecorationTests extends ShellTestCase {
new MockObjectSupplier<>(mMockSurfaceControlTransactions,
() -> mock(SurfaceControl.Transaction.class)),
() -> mMockWindowContainerTransaction, () -> mMockTaskSurface,
- mMockSurfaceControlViewHostFactory, mDesktopModeEventLogger);
+ mMockSurfaceControlViewHostFactory, mMockWindowDecorViewHostSupplier,
+ mDesktopModeEventLogger);
}
private class MockObjectSupplier<T> implements Supplier<T> {
@@ -1049,18 +1075,22 @@ public class WindowDecorationTests extends ShellTestCase {
private class TestWindowDecoration extends WindowDecoration<TestView> {
TestWindowDecoration(Context context, @NonNull Context userContext,
DisplayController displayController,
- ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost>
+ windowDecorViewHostSupplier,
DesktopModeEventLogger desktopModeEventLogger) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
- surfaceControlViewHostFactory, desktopModeEventLogger);
+ surfaceControlViewHostFactory, windowDecorViewHostSupplier,
+ desktopModeEventLogger);
}
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
@@ -1071,8 +1101,12 @@ public class WindowDecorationTests extends ShellTestCase {
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion) {
- relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus,
- displayExclusionRegion);
+ mRelayoutParams.mRunningTaskInfo = taskInfo;
+ mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
+ mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
+ mRelayoutParams.mLayoutResId = R.layout.caption_layout;
+ relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
+ mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
@Override
@@ -1096,13 +1130,8 @@ public class WindowDecorationTests extends ShellTestCase {
void relayout(ActivityManager.RunningTaskInfo taskInfo,
boolean applyStartTransactionOnDraw, boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion) {
- mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- mRelayoutParams.mLayoutResId = R.layout.caption_layout;
- mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
- mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
- relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
- mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+ relayout(taskInfo, hasGlobalFocus, displayExclusionRegion);
}
private AdditionalViewContainer addTestViewContainer() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt
new file mode 100644
index 000000000000..7b42ff4548ac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.windowdecor.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+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.splitscreen.SplitScreenController
+import org.junit.runner.RunWith
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [DesktopMenuPositionUtility].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopMenuPositionUtilityTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopMenuPositionUtilityTest : ShellTestCase() {
+
+ @Mock private val mockSplitScreenController = mock<SplitScreenController>()
+
+ @Test
+ fun testFullscreenPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_FULLSCREEN)
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitLeftPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_TOP_OR_LEFT,
+ isLeftRightSplit = true,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitRightPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ isLeftRightSplit = true,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(
+ CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2) + SPLIT_LEFT_BOUNDS.width(),
+ result.x,
+ )
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitTopPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_TOP_OR_LEFT,
+ isLeftRightSplit = false,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitBottomPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ isLeftRightSplit = false,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP + SPLIT_TOP_BOUNDS.height(), result.y)
+ }
+
+ private fun setupTaskInfo(windowingMode: Int): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder().setWindowingMode(windowingMode).build()
+ }
+
+ private fun setupMockSplitScreenController(isLeftRightSplit: Boolean, splitPosition: Int) {
+ whenever(mockSplitScreenController.getSplitPosition(anyInt())).thenReturn(splitPosition)
+ whenever(mockSplitScreenController.getRefStageBounds(any(), any())).thenAnswer {
+ (it.arguments.first() as Rect).set(
+ if (isLeftRightSplit) {
+ SPLIT_LEFT_BOUNDS
+ } else {
+ SPLIT_TOP_BOUNDS
+ }
+ )
+ (it.arguments[1] as Rect).set(
+ if (isLeftRightSplit) {
+ SPLIT_RIGHT_BOUNDS
+ } else {
+ SPLIT_BOTTOM_BOUNDS
+ }
+ )
+ }
+ whenever(mockSplitScreenController.isLeftRightSplit).thenReturn(isLeftRightSplit)
+ }
+
+ companion object {
+ private val SPLIT_LEFT_BOUNDS = Rect(0, 0, 1280, 1600)
+ private val SPLIT_RIGHT_BOUNDS = Rect(1280, 0, 2560, 1600)
+ private val SPLIT_TOP_BOUNDS = Rect(0, 0, 2560, 800)
+ private val SPLIT_BOTTOM_BOUNDS = Rect(0, 800, 2560, 1600)
+ private const val CAPTION_X = 800
+ private const val CAPTION_Y = 50
+ private const val MARGIN_START = 30
+ private const val MARGIN_TOP = 50
+ private const val MENU_WIDTH = 500
+ private const val CAPTION_WIDTH = 200
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
new file mode 100644
index 000000000000..1ec0fe794d0a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -0,0 +1,224 @@
+/*
+ * 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.windowdecor.common
+
+import android.app.ActivityManager
+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.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import androidx.test.filters.SmallTest
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader.AppResources
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [WindowDecorTaskResourceLoader].
+ *
+ * Build/Install/Run: atest WindowDecorTaskResourceLoaderTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
+ private val testExecutor = TestShellExecutor()
+ private val shellInit = ShellInit(testExecutor)
+ private val mockShellController = mock<ShellController>()
+ private val mockPackageManager = mock<PackageManager>()
+ private val mockIconProvider = mock<IconProvider>()
+ private val mockHeaderIconFactory = mock<BaseIconFactory>()
+ private val mockVeilIconFactory = mock<BaseIconFactory>()
+
+ private lateinit var spyContext: TestableContext
+ private lateinit var loader: WindowDecorTaskResourceLoader
+
+ private val userChangeListenerCaptor = argumentCaptor<UserChangeListener>()
+ private val userChangeListener: UserChangeListener by lazy {
+ userChangeListenerCaptor.firstValue
+ }
+
+ @Before
+ fun setUp() {
+ spyContext = spy(mContext)
+ spyContext.setMockPackageManager(mockPackageManager)
+ doReturn(spyContext).whenever(spyContext).createContextAsUser(any(), anyInt())
+ loader =
+ WindowDecorTaskResourceLoader(
+ context = spyContext,
+ shellInit = shellInit,
+ shellController = mockShellController,
+ shellCommandHandler = mock(),
+ iconProvider = mockIconProvider,
+ headerIconFactory = mockHeaderIconFactory,
+ veilIconFactory = mockVeilIconFactory,
+ )
+ shellInit.init()
+ testExecutor.flushAll()
+ verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture())
+ }
+
+ @Test
+ fun testGetName_notCached_loadsResourceAndCaches() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+
+ loader.getName(task)
+
+ verify(mockPackageManager).getApplicationLabel(task.topActivityInfo!!.applicationInfo)
+ assertThat(loader.taskToResourceCache[task.taskId]?.appName).isNotNull()
+ }
+
+ @Test
+ fun testGetName_cached_returnsFromCache() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+ loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+ loader.getName(task)
+
+ verifyZeroInteractions(
+ mockPackageManager,
+ mockIconProvider,
+ mockHeaderIconFactory,
+ mockVeilIconFactory,
+ )
+ }
+
+ @Test
+ fun testGetHeaderIcon_notCached_loadsResourceAndCaches() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+
+ loader.getHeaderIcon(task)
+
+ verify(mockHeaderIconFactory).createIconBitmap(any(), anyFloat())
+ assertThat(loader.taskToResourceCache[task.taskId]?.appIcon).isNotNull()
+ }
+
+ @Test
+ fun testGetHeaderIcon_cached_returnsFromCache() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+ loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+ loader.getHeaderIcon(task)
+
+ verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+ }
+
+ @Test
+ fun testGetVeilIcon_notCached_loadsResourceAndCaches() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+
+ loader.getVeilIcon(task)
+
+ verify(mockVeilIconFactory).createScaledBitmap(any(), anyInt())
+ assertThat(loader.taskToResourceCache[task.taskId]?.veilIcon).isNotNull()
+ }
+
+ @Test
+ fun testGetVeilIcon_cached_returnsFromCache() {
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+ loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+ loader.getVeilIcon(task)
+
+ verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+ }
+
+ @Test
+ fun testUserChange_updatesContext() {
+ val newUser = 5000
+ val newContext = mock<Context>()
+
+ userChangeListener.onUserChanged(newUser, newContext)
+
+ assertThat(loader.currentUserContext).isEqualTo(newContext)
+ }
+
+ @Test
+ fun testUserChange_clearsCache() {
+ val newUser = 5000
+ val newContext = mock<Context>()
+ val task = createTaskInfo(context.userId)
+ loader.onWindowDecorCreated(task)
+ loader.getName(task)
+
+ userChangeListener.onUserChanged(newUser, newContext)
+
+ assertThat(loader.taskToResourceCache[task.taskId]?.appName).isNull()
+ }
+
+ @Test
+ fun testGet_nonexistentDecor_throws() {
+ val task = createTaskInfo(context.userId)
+
+ assertThrows(Exception::class.java) { loader.getName(task) }
+ }
+
+ private fun createTaskInfo(userId: Int): ActivityManager.RunningTaskInfo {
+ val appIconDrawable = mock<Drawable>()
+ val badgedAppIconDrawable = mock<Drawable>()
+ val activityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
+ val componentName = ComponentName("com.foo", "BarActivity")
+ whenever(mockPackageManager.getActivityInfo(eq(componentName), anyInt()))
+ .thenReturn(activityInfo)
+ whenever(mockPackageManager.getApplicationLabel(activityInfo.applicationInfo))
+ .thenReturn("Test App")
+ whenever(mockPackageManager.getUserBadgedIcon(appIconDrawable, UserHandle.of(userId)))
+ .thenReturn(badgedAppIconDrawable)
+ whenever(mockIconProvider.getIcon(activityInfo)).thenReturn(appIconDrawable)
+ whenever(mockHeaderIconFactory.createIconBitmap(badgedAppIconDrawable, 1f))
+ .thenReturn(mock())
+ whenever(mockVeilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT))
+ .thenReturn(mock())
+ return TestRunningTaskInfoBuilder()
+ .setUserId(userId)
+ .setBaseIntent(Intent().apply { component = componentName })
+ .build()
+ .apply { topActivityInfo = activityInfo }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
index 2f223ded5ce1..4f19f34b8370 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
@@ -18,7 +18,6 @@ package com.android.wm.shell.windowdecor.common.viewhost
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
import androidx.test.filters.SmallTest
@@ -28,7 +27,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -57,54 +55,8 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
onDrawTransaction = null,
)
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
- }
-
- @Test
- fun updateView_alreadyLaidOut_relayouts() = runTest {
- val windowDecorViewHost = createDefaultViewHost()
- val view = View(context)
- windowDecorViewHost.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- val otherParams = WindowManager.LayoutParams(200, 200)
- windowDecorViewHost.updateView(
- view = view,
- attrs = otherParams,
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
- assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
- .isEqualTo(otherParams.width)
- }
-
- @Test
- fun updateView_replacingView_throws() = runTest {
- val windowDecorViewHost = createDefaultViewHost()
- val view = View(context)
- windowDecorViewHost.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- val otherView = View(context)
- assertThrows(Exception::class.java) {
- windowDecorViewHost.updateView(
- view = otherView,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
- }
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isEqualTo(view)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -123,7 +75,7 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
)
// No view host yet, since the coroutine hasn't run.
- assertThat(windowDecorViewHost.viewHost).isNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
windowDecorViewHost.updateView(
view = syncView,
@@ -135,14 +87,13 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
// Would run coroutine if it hadn't been cancelled.
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isNotNull()
// View host view/attrs should match the ones from the sync call, plus, since the
// sync/async were made with different views, if the job hadn't been cancelled there
// would've been an exception thrown as replacing views isn't allowed.
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
- assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
- .isEqualTo(syncAttrs.width)
+ assertThat(windowDecorViewHost.view()).isEqualTo(syncView)
+ assertThat(windowDecorViewHost.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -158,11 +109,11 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
configuration = context.resources.configuration,
)
- assertThat(windowDecorViewHost.viewHost).isNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -185,9 +136,8 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isEqualTo(otherView)
}
@Test
@@ -205,8 +155,7 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
val t = mock(SurfaceControl.Transaction::class.java)
windowDecorViewHost.release(t)
- verify(windowDecorViewHost.viewHost!!).release()
- verify(t).remove(windowDecorViewHost.surfaceControl)
+ verify(windowDecorViewHost.viewHostAdapter).release(t)
}
private fun CoroutineScope.createDefaultViewHost() =
@@ -214,8 +163,8 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
context = context,
mainScope = this,
display = context.display,
- surfaceControlViewHostFactory = { c, d, wwm, s ->
- spy(SurfaceControlViewHost(c, d, wwm, s))
- },
+ viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
)
+
+ private fun DefaultWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt
new file mode 100644
index 000000000000..92f5def508c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.content.res.Configuration
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.util.StubTransaction
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [PooledWindowDecorViewHostSupplier].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:PooledWindowDecorViewHostSupplierTest
+ */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class PooledWindowDecorViewHostSupplierTest : ShellTestCase() {
+
+ private val testExecutor = TestShellExecutor()
+ private val testShellInit = ShellInit(testExecutor)
+
+ private lateinit var supplier: PooledWindowDecorViewHostSupplier
+
+ @Test
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun onInit_warmsAndPoolsViewHosts() = runTest {
+ supplier = createSupplier(maxPoolSize = 5, preWarmSize = 2)
+
+ testExecutor.flushAll()
+ advanceUntilIdle()
+
+ val viewHost1 = supplier.acquire(context, context.display) as ReusableWindowDecorViewHost
+ val viewHost2 = supplier.acquire(context, context.display) as ReusableWindowDecorViewHost
+
+ // Acquired warmed up view hosts from the pool.
+ assertThat(viewHost1.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(viewHost2.viewHostAdapter.isInitialized()).isTrue()
+ }
+
+ @Test(expected = Throwable::class)
+ fun onInit_warmUpSizeExceedsPoolSize_throws() = runTest {
+ createSupplier(maxPoolSize = 3, preWarmSize = 4)
+ }
+
+ @Test
+ fun acquire_poolBelowLimit_caches() = runTest {
+ supplier = createSupplier(maxPoolSize = 5)
+
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction())
+
+ assertThat(supplier.acquire(context, context.display)).isEqualTo(viewHost)
+ }
+
+ @Test
+ fun release_poolBelowLimit_doesNotReleaseViewHost() = runTest {
+ supplier = createSupplier(maxPoolSize = 5)
+
+ val viewHost = FakeWindowDecorViewHost()
+ val mockT = mock<SurfaceControl.Transaction>()
+ supplier.release(viewHost, mockT)
+
+ assertThat(viewHost.released).isFalse()
+ }
+
+ @Test
+ fun release_poolAtLimit_doesNotCache() = runTest {
+ supplier = createSupplier(maxPoolSize = 1)
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction()) // Maxes pool.
+
+ val viewHost2 = FakeWindowDecorViewHost()
+ supplier.release(viewHost2, StubTransaction()) // Beyond limit.
+
+ assertThat(supplier.acquire(context, context.display)).isEqualTo(viewHost)
+ // Second one wasn't cached, so the acquired one should've been a new instance.
+ assertThat(supplier.acquire(context, context.display)).isNotEqualTo(viewHost2)
+ }
+
+ @Test
+ fun release_poolAtLimit_releasesViewHost() = runTest {
+ supplier = createSupplier(maxPoolSize = 1)
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction()) // Maxes pool.
+
+ val viewHost2 = FakeWindowDecorViewHost()
+ val mockT = mock<SurfaceControl.Transaction>()
+ supplier.release(viewHost2, mockT) // Beyond limit.
+
+ // Second one doesn't fit, so it needs to be released.
+ assertThat(viewHost2.released).isTrue()
+ }
+
+ private fun CoroutineScope.createSupplier(maxPoolSize: Int, preWarmSize: Int = 0) =
+ PooledWindowDecorViewHostSupplier(context, this, testShellInit, maxPoolSize, preWarmSize)
+ .also { testShellInit.init() }
+
+ private class FakeWindowDecorViewHost : WindowDecorViewHost {
+ var released = false
+ private set
+
+ override val surfaceControl: SurfaceControl
+ get() = SurfaceControl()
+
+ override fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {}
+
+ override fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ ) {}
+
+ override fun release(t: SurfaceControl.Transaction) {
+ released = true
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt
new file mode 100644
index 000000000000..d99a4825e580
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt
@@ -0,0 +1,178 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [ReusableWindowDecorViewHost].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:ReusableWindowDecorViewHostTest
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class ReusableWindowDecorViewHostTest : ShellTestCase() {
+
+ @Test
+ fun update_differentView_replacesView() = runTest {
+ val view = View(context)
+ val lp = WindowManager.LayoutParams()
+ val reusableVH = createReusableViewHost()
+ reusableVH.updateView(view, lp, context.resources.configuration, null)
+
+ assertThat(reusableVH.rootView.childCount).isEqualTo(1)
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(view)
+
+ val newView = View(context)
+ val newLp = WindowManager.LayoutParams()
+ reusableVH.updateView(newView, newLp, context.resources.configuration, null)
+
+ assertThat(reusableVH.rootView.childCount).isEqualTo(1)
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(newView)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateView_clearsPendingAsyncJob() = runTest {
+ val reusableVH = createReusableViewHost()
+ val asyncView = View(context)
+ val syncView = View(context)
+ val asyncAttrs = WindowManager.LayoutParams(100, 100)
+ val syncAttrs = WindowManager.LayoutParams(200, 200)
+
+ reusableVH.updateViewAsync(
+ view = asyncView,
+ attrs = asyncAttrs,
+ configuration = context.resources.configuration,
+ )
+
+ // No view host yet, since the coroutine hasn't run.
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
+
+ reusableVH.updateView(
+ view = syncView,
+ attrs = syncAttrs,
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ // Would run coroutine if it hadn't been cancelled.
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ // View host view/attrs should match the ones from the sync call.
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(syncView)
+ assertThat(reusableVH.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync() = runTest {
+ val reusableVH = createReusableViewHost()
+ val view = View(context)
+ val attrs = WindowManager.LayoutParams(100, 100)
+
+ reusableVH.updateViewAsync(
+ view = view,
+ attrs = attrs,
+ configuration = context.resources.configuration,
+ )
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
+
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync_clearsPendingAsyncJob() = runTest {
+ val reusableVH = createReusableViewHost()
+
+ val view = View(context)
+ reusableVH.updateViewAsync(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+ val otherView = View(context)
+ reusableVH.updateViewAsync(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(otherView)
+ }
+
+ @Test
+ fun release() = runTest {
+ val reusableVH = createReusableViewHost()
+
+ val view = View(context)
+ reusableVH.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ val t = mock(SurfaceControl.Transaction::class.java)
+ reusableVH.release(t)
+
+ verify(reusableVH.viewHostAdapter).release(t)
+ }
+
+ @Test
+ fun warmUp_addsRootView() = runTest {
+ val reusableVH = createReusableViewHost().apply { warmUp() }
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(reusableVH.view()).isEqualTo(reusableVH.rootView)
+ }
+
+ private fun CoroutineScope.createReusableViewHost() =
+ ReusableWindowDecorViewHost(
+ context = context,
+ mainScope = this,
+ display = context.display,
+ id = 1,
+ viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
+ )
+
+ private fun ReusableWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt
new file mode 100644
index 000000000000..5109a7c300f6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt
@@ -0,0 +1,144 @@
+/*
+ * 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.windowdecor.common.viewhost
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [SurfaceControlViewHostAdapter].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SurfaceControlViewHostAdapterTest
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class SurfaceControlViewHostAdapterTest : ShellTestCase() {
+
+ private lateinit var adapter: SurfaceControlViewHostAdapter
+
+ @Before
+ fun setUp() {
+ adapter = SurfaceControlViewHostAdapter(
+ context,
+ context.display,
+ surfaceControlViewHostFactory = { c, d, wwm, s ->
+ spy(SurfaceControlViewHost(c, d, wwm, s))
+ }
+ )
+ }
+
+ @Test
+ fun prepareViewHost() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ assertThat(adapter.viewHost).isNotNull()
+ }
+
+ @Test
+ fun prepareViewHost_alreadyCreated_skips() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ val viewHost = adapter.viewHost!!
+
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ assertThat(adapter.viewHost).isEqualTo(viewHost)
+ }
+
+ @Test
+ fun updateView_layoutInViewHost() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ assertThat(adapter.isInitialized()).isTrue()
+ assertThat(adapter.view()).isEqualTo(view)
+ }
+
+ @Test
+ fun updateView_alreadyLaidOut_relayouts() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val otherParams = WindowManager.LayoutParams(200, 200)
+ adapter.updateView(
+ view = view,
+ attrs = otherParams
+ )
+
+ assertThat(adapter.view()).isEqualTo(view)
+ assertThat(adapter.view()!!.layoutParams.width).isEqualTo(otherParams.width)
+ }
+
+ @Test
+ fun updateView_replacingView_throws() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val otherView = View(context)
+ assertThrows(Exception::class.java) {
+ adapter.updateView(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+ }
+ }
+
+ @Test
+ fun release() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = View(context),
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val mockT = mock(SurfaceControl.Transaction::class.java)
+ adapter.release(mockT)
+
+ verify(adapter.viewHost!!).release()
+ verify(mockT).remove(adapter.rootSurface)
+ }
+
+ private fun SurfaceControlViewHostAdapter.view(): View? = viewHost?.view
+} \ No newline at end of file
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 193c2c25d26d..997ece6ecadc 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
@@ -32,7 +32,10 @@ import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,6 +50,8 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val contextMock: Context = mock()
+ private val mainDispatcher: MainCoroutineDispatcher = mock()
+ private val bgScope: CoroutineScope = mock()
private val displayControllerMock: DisplayController = mock()
private val rootTdaOrganizerMock: RootTaskDisplayAreaOrganizer = mock()
private val syncQueueMock: SyncTransactionQueue = mock()
@@ -61,6 +66,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val desktopModeWindowDecorationMock: DesktopModeWindowDecoration = mock()
private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
+ private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
@Before
@@ -68,6 +74,8 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
desktopTilingDecorViewModel =
DesktopTilingDecorViewModel(
contextMock,
+ mainDispatcher,
+ bgScope,
displayControllerMock,
rootTdaOrganizerMock,
syncQueueMock,
@@ -77,6 +85,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
returnToDragStartAnimatorMock,
userRepositories,
desktopModeEventLogger,
+ taskResourceLoader,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
}
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 95e2151be96c..2f15c2e38855 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
@@ -45,7 +45,10 @@ import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -99,6 +102,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
private val motionEvent: MotionEvent = mock()
private val desktopRepository: DesktopRepository = mock()
+ private val mainDispatcher: MainCoroutineDispatcher = mock()
+ private val bgScope: CoroutineScope = mock()
+ private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
private lateinit var tilingDecoration: DesktopTilingWindowDecoration
private val split_divider_width = 10
@@ -110,8 +116,11 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
tilingDecoration =
DesktopTilingWindowDecoration(
context,
+ mainDispatcher,
+ bgScope,
syncQueue,
displayController,
+ taskResourceLoader,
displayId,
rootTdaOrganizer,
transitions,
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 5e645cceea2d..a592749c5398 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -38,7 +38,7 @@ CursorWindow::CursorWindow() {
}
CursorWindow::~CursorWindow() {
- if (mAshmemFd != -1) {
+ if (mAshmemFd >= 0) {
::munmap(mData, mSize);
::close(mAshmemFd);
} else {
@@ -155,23 +155,27 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow
bool isAshmem;
if (parcel->readBool(&isAshmem)) goto fail;
if (isAshmem) {
- window->mAshmemFd = parcel->readFileDescriptor();
- if (window->mAshmemFd < 0) {
+ int tempFd = parcel->readFileDescriptor();
+ if (tempFd < 0) {
LOG(ERROR) << "Failed readFileDescriptor";
goto fail_silent;
}
- window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0);
- if (window->mAshmemFd < 0) {
+ tempFd = ::fcntl(tempFd, F_DUPFD_CLOEXEC, 0);
+ if (tempFd < 0) {
PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
goto fail_silent;
}
- window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
+ window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, tempFd, 0);
if (window->mData == MAP_FAILED) {
+ ::close(tempFd);
PLOG(ERROR) << "Failed mmap";
goto fail_silent;
}
+
+ window->mAshmemFd = tempFd;
+
} else {
window->mAshmemFd = -1;
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index de402095e195..139ccfd22b0e 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -16,6 +16,7 @@ package com.android.extensions.appfunctions {
field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final int ERROR_DENIED = 1000; // 0x3e8
field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002; // 0x7d2
field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
index 2540236f2ce5..0c521690b165 100644
--- a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
@@ -71,6 +71,13 @@ public final class AppFunctionException extends Exception {
public static final int ERROR_CANCELLED = 2001;
/**
+ * The operation was disallowed by enterprise policy.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002;
+
+ /**
* An unknown error occurred while processing the call in the AppFunctionService.
*
* <p>This error is thrown when the service is connected in the remote application but an
@@ -189,7 +196,8 @@ public final class AppFunctionException extends Exception {
ERROR_SYSTEM_ERROR,
ERROR_INVALID_ARGUMENT,
ERROR_DISABLED,
- ERROR_CANCELLED
+ ERROR_CANCELLED,
+ ERROR_ENTERPRISE_POLICY_DISALLOWED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e2db2c9e3827..677fd86aca9c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -154,7 +154,10 @@ cc_defaults {
"libstatssocket_lazy",
"libtonemap",
],
- whole_static_libs: ["hwui_flags_cc_lib"],
+ whole_static_libs: [
+ "hwui_flags_cc_lib",
+ "libsurfaceflingerflags",
+ ],
},
host: {
static_libs: [
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index cc292d9de7b2..b1550b0b6888 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -19,6 +19,7 @@
#include "HardwareBitmapUploader.h"
#include "Properties.h"
#ifdef __ANDROID__ // Layoutlib does not support render thread
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <private/android/AHardwareBufferHelpers.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>
@@ -562,7 +563,7 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr,
bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) {
#ifdef __ANDROID__ // TODO: This isn't built for host for some reason?
- if (hasGainmap() && format == JavaCompressFormat::Jpeg) {
+ if (hasGainmap()) {
SkBitmap baseBitmap = getSkBitmap();
SkBitmap gainmapBitmap = gainmap()->bitmap->getSkBitmap();
if (gainmapBitmap.colorType() == SkColorType::kAlpha_8_SkColorType) {
@@ -572,12 +573,27 @@ bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* str
greyGainmap.setPixelRef(sk_ref_sp(gainmapBitmap.pixelRef()), 0, 0);
gainmapBitmap = std::move(greyGainmap);
}
- SkJpegEncoder::Options options{.fQuality = quality};
- return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options,
- gainmapBitmap.pixmap(), options, gainmap()->info);
+ switch (format) {
+ case JavaCompressFormat::Jpeg: {
+ SkJpegEncoder::Options options{.fQuality = quality};
+ return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options,
+ gainmapBitmap.pixmap(), options,
+ gainmap()->info);
+ }
+ case JavaCompressFormat::Png: {
+ if (com::android::graphics::surfaceflinger::flags::true_hdr_screenshots()) {
+ SkGainmapInfo info = gainmap()->info;
+ SkPngEncoder::Options options{.fGainmap = &gainmapBitmap.pixmap(),
+ .fGainmapInfo = &info};
+ return SkPngEncoder::Encode(stream, baseBitmap.pixmap(), options);
+ }
+ // fallthrough if we're not supporting HDR screenshots
+ }
+ default:
+ ALOGI("Format: %d doesn't support gainmap compression!", format);
+ }
}
#endif
-
SkBitmap skbitmap;
getSkBitmap(&skbitmap);
return compress(skbitmap, format, quality, stream);
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index c73598960551..d1782b285b34 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -225,8 +225,8 @@ static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong
constexpr float NO_OVERRIDE = -1;
-float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) {
- for (const minikin::FontVariation& fv : fakery.variationSettings()) {
+float findValueFromVariationSettings(const minikin::VariationSettings& axes, minikin::AxisTag tag) {
+ for (const minikin::FontVariation& fv : axes) {
if (fv.axisTag == tag) {
return fv.value;
}
@@ -238,8 +238,8 @@ float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin:
static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
if (text_feature::typeface_redesign_readonly()) {
- float value =
- findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
+ float value = findValueFromVariationSettings(layout->layout.typeface(i)->GetAxes(),
+ minikin::TAG_wght);
return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).wghtAdjustment();
@@ -250,8 +250,8 @@ static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlon
static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
if (text_feature::typeface_redesign_readonly()) {
- float value =
- findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
+ float value = findValueFromVariationSettings(layout->layout.typeface(i)->GetAxes(),
+ minikin::TAG_ital);
return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).italAdjustment();
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 9478e350de57..ba4224137cd4 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -6,6 +6,111 @@ package android.location {
method public void onLocationBatch(java.util.List<android.location.Location>);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel 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();
+ method @NonNull public java.util.List<android.location.BeidouSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouAssistance> CREATOR;
+ }
+
+ public static final class BeidouAssistance.Builder {
+ ctor public BeidouAssistance.Builder();
+ method @NonNull public android.location.BeidouAssistance build();
+ method @NonNull public android.location.BeidouAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.BeidouAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.BeidouAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.BeidouAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.BeidouSatelliteEphemeris>);
+ method @NonNull public android.location.BeidouAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=1, to=63) public int getPrn();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.00977F, to=0.00977f) public double getAf0();
+ method @FloatRange(from=-1.87E-9F, to=1.87E-9f) public double getAf1();
+ method @FloatRange(from=-1.39E-17F, to=1.39E-17f) public double getAf2();
+ method @IntRange(from=0, to=31) public int getAodc();
+ method @FloatRange(from=-5.12E-8F, to=5.12E-8f) public double getTgd1();
+ method @FloatRange(from=-5.12E-8F, to=5.12E-8f) public double getTgd2();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder {
+ ctor public BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.00977F, to=0.00977f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf1(@FloatRange(from=-1.87E-9F, to=1.87E-9f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf2(@FloatRange(from=-1.39E-17F, to=1.39E-17f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAodc(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTgd1(@FloatRange(from=-5.12E-8F, to=5.12E-8f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTgd2(@FloatRange(from=-5.12E-8F, to=5.12E-8f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime implements android.os.Parcelable {
+ method public int describeContents();
+ 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;
+ }
+
+ 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 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);
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1) public int getSatH1();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getSvAccur();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder {
+ ctor public BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder setSatH1(int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder setSvAccur(double);
+ }
+
+ public static final class BeidouSatelliteEphemeris.Builder {
+ ctor public BeidouSatelliteEphemeris.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setPrn(int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
public final class CorrelationVector implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0f) public double getFrequencyOffsetMetersPerSecond();
@@ -43,12 +148,375 @@ package android.location {
method public void unregisterCountryDetectorCallback(@NonNull java.util.function.Consumer<android.location.Country>);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel 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();
+ method @NonNull public java.util.List<android.location.GalileoSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoAssistance> CREATOR;
+ }
+
+ public static final class GalileoAssistance.Builder {
+ ctor public GalileoAssistance.Builder();
+ 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 setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GalileoSatelliteEphemeris>);
+ method @NonNull public android.location.GalileoAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoIonosphericModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0f, to=512.0f) public double getAi0();
+ method @FloatRange(from=-4.0F, to=4.0f) public double getAi1();
+ method @FloatRange(from=-0.5F, to=0.5f) public double getAi2();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoIonosphericModel> CREATOR;
+ }
+
+ public static final class GalileoIonosphericModel.Builder {
+ ctor public GalileoIonosphericModel.Builder();
+ method @NonNull public android.location.GalileoIonosphericModel build();
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi0(@FloatRange(from=0.0f, to=512.0f) double);
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi1(@FloatRange(from=-4.0F, to=4.0f) double);
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi2(@FloatRange(from=-0.5F, to=0.5f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel> getSatelliteClockModels();
+ method @IntRange(from=1, to=36) public int getSatelliteCodeNumber();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GalileoSatelliteEphemeris.Builder {
+ ctor public GalileoSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteClockModels(@NonNull java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel>);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteCodeNumber(@IntRange(from=1, to=36) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GalileoSatelliteEphemeris.GalileoSvHealth);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.0625F, to=0.0625f) public double getAf0();
+ method @FloatRange(from=-1.5E-8F, to=1.5E-8f) public double getAf1();
+ method @FloatRange(from=-5.56E-17F, to=5.56E-17f) public double getAf2();
+ method @FloatRange(from=-1.2E-7F, to=1.2E-7f) public double getBgdSeconds();
+ method public int getSatelliteClockType();
+ method @FloatRange(from=0.0f) public double getSisaMeters();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel> CREATOR;
+ field public static final int TYPE_FNAV = 1; // 0x1
+ field public static final int TYPE_INAV = 2; // 0x2
+ field public static final int TYPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder {
+ ctor public GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.0625F, to=0.0625f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf1(@FloatRange(from=-1.5E-8F, to=1.5E-8f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf2(@FloatRange(from=-5.56E-17F, to=5.56E-17f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setBgdSeconds(@FloatRange(from=-1.2E-7F, to=1.2E-7f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setSatelliteClockType(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setSisaMeters(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSvHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE1b();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE5a();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE5b();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE1b();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE5a();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE5b();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris.GalileoSvHealth> CREATOR;
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSvHealth.Builder {
+ ctor public GalileoSatelliteEphemeris.GalileoSvHealth.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE1b(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5a(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5b(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE1b(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5a(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5b(@IntRange(from=0, to=3) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAlmanac implements android.os.Parcelable {
+ ctor public GlonassAlmanac(@IntRange(from=0) long, @NonNull java.util.List<android.location.GlonassAlmanac.GlonassSatelliteAlmanac>);
+ method public int describeContents();
+ method @IntRange(from=0) public long getIssueDateMillis();
+ method @NonNull public java.util.List<android.location.GlonassAlmanac.GlonassSatelliteAlmanac> getSatelliteAlmanacs();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAlmanac> CREATOR;
+ }
+
+ public static final class GlonassAlmanac.GlonassSatelliteAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.067F, to=0.067f) public double getDeltaI();
+ method @FloatRange(from=-3600.0F, to=3600.0f) public double getDeltaT();
+ method @FloatRange(from=-0.004F, to=0.004f) public double getDeltaTDot();
+ method @FloatRange(from=0.0f, to=0.03f) public double getEccentricity();
+ method @IntRange(from=0, to=31) public int getFreqChannel();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getLambda();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega();
+ method @IntRange(from=1, to=25) public int getSlotNumber();
+ method @IntRange(from=0, to=1) public int getSvHealth();
+ method @FloatRange(from=0.0f, to=44100.0f) public double getTLambda();
+ method @FloatRange(from=-0.0019F, to=0.0019f) public double getTau();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAlmanac.GlonassSatelliteAlmanac> CREATOR;
+ }
+
+ public static final class GlonassAlmanac.GlonassSatelliteAlmanac.Builder {
+ ctor public GlonassAlmanac.GlonassSatelliteAlmanac.Builder();
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac build();
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaI(@FloatRange(from=-0.067F, to=0.067f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaT(@FloatRange(from=-3600.0F, to=3600.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaTDot(@FloatRange(from=-0.004F, to=0.004f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setEccentricity(@FloatRange(from=0.0f, to=0.03f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setFreqChannel(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setLambda(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setOmega(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSlotNumber(@IntRange(from=1, to=25) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSvHealth(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTLambda(@FloatRange(from=0.0f, to=44100.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTau(@FloatRange(from=-0.0019F, to=0.0019f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GlonassAlmanac getAlmanac();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.GlonassSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAssistance> CREATOR;
+ }
+
+ public static final class GlonassAssistance.Builder {
+ ctor public GlonassAssistance.Builder();
+ method @NonNull public android.location.GlonassAssistance build();
+ method @NonNull public android.location.GlonassAssistance.Builder setAlmanac(@Nullable android.location.GlonassAlmanac);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GlonassSatelliteEphemeris>);
+ method @NonNull public android.location.GlonassAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GlonassAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=31) public int getAgeInDays();
+ method @FloatRange(from=0.0f) public double getFrameTimeSeconds();
+ method @IntRange(from=0, to=1) public int getHealthState();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=1, to=25) public int getSlotNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.Builder {
+ ctor public GlonassSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setAgeInDays(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setFrameTimeSeconds(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setHealthState(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSlotNumber(@IntRange(from=1, to=25) int);
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.002F, to=0.002f) public double getClockBias();
+ method @FloatRange(from=-9.32E-10F, to=9.32E-10f) public double getFrequencyBias();
+ method @IntRange(from=0xfffffff9, to=6) public int getFrequencyNumber();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder {
+ ctor public GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setClockBias(@FloatRange(from=-0.002F, to=0.002f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyBias(@FloatRange(from=-9.32E-10F, to=9.32E-10f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyNumber(@IntRange(from=0xfffffff9, to=6) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getX();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getXAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getXDot();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getY();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getYAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getYDot();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getZ();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getZAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getZDot();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder {
+ ctor public GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setX(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setXAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setXDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setY(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setYAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setYDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZ(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac> getGnssSatelliteAlmanacs();
+ method @IntRange(from=0) public int getIod();
+ method @IntRange(from=0) public long getIssueDateMillis();
+ method @IntRange(from=0, to=604800) public int getToaSeconds();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAlmanac> CREATOR;
+ }
+
+ public static final class GnssAlmanac.Builder {
+ ctor public GnssAlmanac.Builder();
+ method @NonNull public android.location.GnssAlmanac build();
+ method @NonNull public android.location.GnssAlmanac.Builder setGnssSatelliteAlmanacs(@NonNull java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac>);
+ method @NonNull public android.location.GnssAlmanac.Builder setIod(@IntRange(from=0) int);
+ method @NonNull public android.location.GnssAlmanac.Builder setIssueDateMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.GnssAlmanac.Builder setToaSeconds(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.GnssAlmanac.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
+ public static final class GnssAlmanac.GnssSatelliteAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.0625F, to=0.0625f) public double getAf0();
+ method @FloatRange(from=-1.5E-8F, to=1.5E-8f) public double getAf1();
+ method @FloatRange(from=0.0f) public double getEccentricity();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getInclination();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getM0();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega0();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmegaDot();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getRootA();
+ method @IntRange(from=0) public int getSvHealth();
+ method @IntRange(from=1) public int getSvid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAlmanac.GnssSatelliteAlmanac> CREATOR;
+ }
+
+ public static final class GnssAlmanac.GnssSatelliteAlmanac.Builder {
+ ctor public GnssAlmanac.GnssSatelliteAlmanac.Builder();
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac build();
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setAf0(@FloatRange(from=-0.0625F, to=0.0625f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setAf1(@FloatRange(from=-1.5E-8F, to=1.5E-8f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setEccentricity(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setInclination(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setM0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmega(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmega0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmegaDot(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setRootA(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setSvHealth(@IntRange(from=0) int);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setSvid(@IntRange(from=1) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.BeidouAssistance getBeidouAssistance();
+ method @Nullable public android.location.GalileoAssistance getGalileoAssistance();
+ method @Nullable public android.location.GlonassAssistance getGlonassAssistance();
+ method @Nullable public android.location.GpsAssistance getGpsAssistance();
+ method @Nullable public android.location.QzssAssistance getQzssAssistance();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAssistance> CREATOR;
+ }
+
+ public static final class GnssAssistance.Builder {
+ ctor public GnssAssistance.Builder();
+ method @NonNull public android.location.GnssAssistance build();
+ method @NonNull public android.location.GnssAssistance.Builder setBeidouAssistance(@Nullable android.location.BeidouAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGalileoAssistance(@Nullable android.location.GalileoAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGlonassAssistance(@Nullable android.location.GlonassAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGpsAssistance(@Nullable android.location.GpsAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setQzssAssistance(@Nullable android.location.QzssAssistance);
+ }
+
+ public static final class GnssAssistance.GnssSatelliteCorrections implements android.os.Parcelable {
+ ctor public GnssAssistance.GnssSatelliteCorrections(@IntRange(from=1, to=206) int, @NonNull java.util.List<android.location.IonosphericCorrection>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.IonosphericCorrection> getIonosphericCorrections();
+ method @IntRange(from=1, to=206) public int getSvid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAssistance.GnssSatelliteCorrections> CREATOR;
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
method @Deprecated public boolean hasNavMessages();
method @Deprecated public boolean hasSatelliteBlacklist();
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssCorrectionComponent implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent(@NonNull String, @NonNull android.location.GnssCorrectionComponent.GnssInterval, @NonNull android.location.GnssCorrectionComponent.PseudorangeCorrection);
+ method public int describeContents();
+ method @NonNull public android.location.GnssCorrectionComponent.PseudorangeCorrection getPseudorangeCorrection();
+ method @NonNull public String getSourceKey();
+ method @NonNull public android.location.GnssCorrectionComponent.GnssInterval getValidityInterval();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent> CREATOR;
+ }
+
+ public static final class GnssCorrectionComponent.GnssInterval implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent.GnssInterval(@IntRange(from=0) long, @IntRange(from=0) long);
+ method public int describeContents();
+ method @IntRange(from=0) public long getEndMillisSinceGpsEpoch();
+ method @IntRange(from=0) public long getStartMillisSinceGpsEpoch();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent.GnssInterval> CREATOR;
+ }
+
+ public static final class GnssCorrectionComponent.PseudorangeCorrection implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent.PseudorangeCorrection(double, double, double);
+ method public int describeContents();
+ method public double getCorrectionMeters();
+ method public double getCorrectionRateMetersPerSecond();
+ method @FloatRange(from=0.0f) public double getCorrectionUncertaintyMeters();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent.PseudorangeCorrection> CREATOR;
+ }
+
public final class GnssExcessPathInfo implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0f) public float getAttenuationDb();
@@ -193,6 +661,33 @@ package android.location {
method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel 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();
+ method @NonNull public java.util.List<android.location.GpsSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsAssistance> CREATOR;
+ }
+
+ public static final class GpsAssistance.Builder {
+ ctor public GpsAssistance.Builder();
+ method @NonNull public android.location.GpsAssistance build();
+ method @NonNull public android.location.GpsAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.GpsAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GpsAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.GpsAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GpsSatelliteEphemeris>);
+ method @NonNull public android.location.GpsAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
@Deprecated public class GpsClock implements android.os.Parcelable {
method @Deprecated public int describeContents();
method @Deprecated public double getBiasInNs();
@@ -418,6 +913,174 @@ package android.location {
method @Deprecated public void onStatusChanged(int);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
+ method @IntRange(from=1, to=32) public int getPrn();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.Builder {
+ ctor public GpsSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setPrn(@IntRange(from=1, to=32) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsL2Params implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=3) public int getL2Code();
+ method @IntRange(from=0, to=1) public int getL2Flag();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsL2Params> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsL2Params.Builder {
+ ctor public GpsSatelliteEphemeris.GpsL2Params.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params.Builder setL2Code(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params.Builder setL2Flag(@IntRange(from=0, to=1) int);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.00977F, to=0.00977f) public double getAf0();
+ method @FloatRange(from=-3.73E-9F, to=3.73E-9f) public double getAf1();
+ method @FloatRange(from=-3.56E-15F, to=3.56E-15f) public double getAf2();
+ method @IntRange(from=0, to=1023) public int getIodc();
+ method @FloatRange(from=-5.97E-8F, to=5.97E-8f) public double getTgd();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder {
+ ctor public GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.00977F, to=0.00977f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf1(@FloatRange(from=-3.73E-9F, to=3.73E-9f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf2(@FloatRange(from=-3.56E-15F, to=3.56E-15f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setIodc(@IntRange(from=0, to=1023) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setTgd(@FloatRange(from=-5.97E-8F, to=5.97E-8f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0f) public double getFitInt();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getSvAccur();
+ method @IntRange(from=0, to=63) public int getSvHealth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsSatelliteHealth> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteHealth.Builder {
+ ctor public GpsSatelliteEphemeris.GpsSatelliteHealth.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setFitInt(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setSvAccur(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setSvHealth(@IntRange(from=0, to=63) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class IonosphericCorrection implements android.os.Parcelable {
+ ctor public IonosphericCorrection(@IntRange(from=0) long, @NonNull android.location.GnssCorrectionComponent);
+ method public int describeContents();
+ method @IntRange(from=0) public long getCarrierFrequencyHz();
+ method @NonNull public android.location.GnssCorrectionComponent getIonosphericCorrection();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.IonosphericCorrection> CREATOR;
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class KeplerianOrbitModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.18E-8F, to=1.18E-8f) public double getDeltaN();
+ method @FloatRange(from=0.0f, to=0.5f) public double getEccentricity();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getI0();
+ method @FloatRange(from=-2.94E-9F, to=2.94E-9f) public double getIDot();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getM0();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getOmega();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getOmega0();
+ method @FloatRange(from=-3.1E-6F, to=3.1E-6f) public double getOmegaDot();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getRootA();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation getSecondOrderHarmonicPerturbation();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KeplerianOrbitModel> CREATOR;
+ }
+
+ public static final class KeplerianOrbitModel.Builder {
+ ctor public KeplerianOrbitModel.Builder();
+ method @NonNull public android.location.KeplerianOrbitModel build();
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setDeltaN(@FloatRange(from=-1.18E-8F, to=1.18E-8f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setEccentricity(@FloatRange(from=0.0f, to=0.5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setI0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setIDot(@FloatRange(from=-2.94E-9F, to=2.94E-9f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setM0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmega(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmega0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmegaDot(@FloatRange(from=-3.1E-6F, to=3.1E-6f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setRootA(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setSecondOrderHarmonicPerturbation(@NonNull android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation);
+ }
+
+ public static final class KeplerianOrbitModel.SecondOrderHarmonicPerturbation implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCic();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCis();
+ method @FloatRange(from=-2048.0F, to=2048.0f) public double getCrc();
+ method @FloatRange(from=-2048.0F, to=2048.0f) public double getCrs();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCuc();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation> CREATOR;
+ }
+
+ public static final class KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder {
+ ctor public KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation build();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCic(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCis(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCrc(@FloatRange(from=-2048.0F, to=2048.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCrs(@FloatRange(from=-2048.0F, to=2048.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCuc(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCus(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class KlobucharIonosphericModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.193E-7F, to=1.193E-7f) public double getAlpha0();
+ method @FloatRange(from=-9.54E-7F, to=9.54E-7f) public double getAlpha1();
+ method @FloatRange(from=-7.63E-6F, to=7.63E-6f) public double getAlpha2();
+ method @FloatRange(from=-7.63E-6F, to=7.63E-6f) public double getAlpha3();
+ method @FloatRange(from=-262144.0F, to=262144.0f) public double getBeta0();
+ method @FloatRange(from=-2097152.0F, to=2097152.0f) public double getBeta1();
+ method @FloatRange(from=-8388608.0F, to=8388608.0f) public double getBeta2();
+ method @FloatRange(from=-8388608.0F, to=8388608.0f) public double getBeta3();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KlobucharIonosphericModel> CREATOR;
+ }
+
+ public static final class KlobucharIonosphericModel.Builder {
+ ctor public KlobucharIonosphericModel.Builder();
+ method @NonNull public android.location.KlobucharIonosphericModel build();
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha0(@FloatRange(from=-1.193E-7F, to=1.193E-7f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha1(@FloatRange(from=-9.54E-7F, to=9.54E-7f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha2(@FloatRange(from=-7.63E-6F, to=7.63E-6f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha3(@FloatRange(from=-7.63E-6F, to=7.63E-6f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta0(@FloatRange(from=-262144.0F, to=262144.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta1(@FloatRange(from=-2097152.0F, to=2097152.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta2(@FloatRange(from=-8388608.0F, to=8388608.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta3(@FloatRange(from=-8388608.0F, to=8388608.0f) double);
+ }
+
public final class LastLocationRequest implements android.os.Parcelable {
method public int describeContents();
method public boolean isAdasGnssBypass();
@@ -436,6 +1099,25 @@ package android.location {
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_BYPASS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class LeapSecondsModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getDayNumberLeapSecondsFuture();
+ method @IntRange(from=0) public int getLeapSeconds();
+ method @IntRange(from=0) public int getLeapSecondsFuture();
+ method @IntRange(from=0) public int getWeekNumberLeapSecondsFuture();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.LeapSecondsModel> CREATOR;
+ }
+
+ public static final class LeapSecondsModel.Builder {
+ ctor public LeapSecondsModel.Builder();
+ method @NonNull public android.location.LeapSecondsModel build();
+ method @NonNull public android.location.LeapSecondsModel.Builder setDayNumberLeapSecondsFuture(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setLeapSeconds(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setLeapSecondsFuture(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setWeekNumberLeapSecondsFuture(@IntRange(from=0) int);
+ }
+
public class LocationManager {
method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
@@ -513,6 +1195,98 @@ package android.location {
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel 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();
+ method @NonNull public java.util.List<android.location.QzssSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.QzssAssistance> CREATOR;
+ }
+
+ public static final class QzssAssistance.Builder {
+ ctor public QzssAssistance.Builder();
+ method @NonNull public android.location.QzssAssistance build();
+ method @NonNull public android.location.QzssAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.QzssAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.QzssAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.QzssAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.QzssSatelliteEphemeris>);
+ method @NonNull public android.location.QzssAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
+ method @IntRange(from=183, to=206) public int getPrn();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.QzssSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class QzssSatelliteEphemeris.Builder {
+ ctor public QzssSatelliteEphemeris.Builder();
+ method @NonNull public android.location.QzssSatelliteEphemeris build();
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setPrn(@IntRange(from=183, to=206) int);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class RealTimeIntegrityModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getAdvisoryNumber();
+ method @NonNull public String getAdvisoryType();
+ method @IntRange(from=0) public long getEndDateSeconds();
+ method @IntRange(from=0) public long getPublishDateSeconds();
+ method @IntRange(from=0) public long getStartDateSeconds();
+ method @IntRange(from=1, to=206) public int getSvid();
+ method public boolean isUsable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.RealTimeIntegrityModel> CREATOR;
+ }
+
+ public static final class RealTimeIntegrityModel.Builder {
+ ctor public RealTimeIntegrityModel.Builder();
+ method @NonNull public android.location.RealTimeIntegrityModel build();
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryNumber(@NonNull String);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryType(@NonNull String);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setEndDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setPublishDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setStartDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setSvid(@IntRange(from=1, to=206) int);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setUsable(boolean);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class SatelliteEphemerisTime implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1023) public int getIode();
+ method @IntRange(from=0, to=604799) public int getToeSeconds();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.SatelliteEphemerisTime> CREATOR;
+ }
+
+ public static final class SatelliteEphemerisTime.Builder {
+ ctor public SatelliteEphemerisTime.Builder();
+ method @NonNull public android.location.SatelliteEphemerisTime build();
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setIode(@IntRange(from=0, to=1023) int);
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604799) int);
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
public final class SatellitePvt implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.SatellitePvt.ClockInfo getClockInfo();
@@ -587,6 +1361,46 @@ package android.location {
field @NonNull public static final android.os.Parcelable.Creator<android.location.SatellitePvt.VelocityEcef> CREATOR;
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class TimeModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getA0();
+ method @FloatRange(from=-3.28E-6F, to=3.28E-6f) public double getA1();
+ method @IntRange(from=0, to=604800) public int getTimeOfWeek();
+ method public int getToGnss();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.TimeModel> CREATOR;
+ }
+
+ public static final class TimeModel.Builder {
+ ctor public TimeModel.Builder();
+ method @NonNull public android.location.TimeModel build();
+ method @NonNull public android.location.TimeModel.Builder setA0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.TimeModel.Builder setA1(@FloatRange(from=-3.28E-6F, to=3.28E-6f) double);
+ method @NonNull public android.location.TimeModel.Builder setTimeOfWeek(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.TimeModel.Builder setToGnss(int);
+ method @NonNull public android.location.TimeModel.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class UtcModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-2.0F, to=2.0f) public double getA0();
+ method @FloatRange(from=-7.45E-9F, to=7.45E-9f) public double getA1();
+ method @IntRange(from=0, to=604800) public int getTimeOfWeek();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.UtcModel> CREATOR;
+ }
+
+ public static final class UtcModel.Builder {
+ ctor public UtcModel.Builder();
+ method @NonNull public android.location.UtcModel build();
+ method @NonNull public android.location.UtcModel.Builder setA0(@FloatRange(from=-2.0F, to=2.0f) double);
+ method @NonNull public android.location.UtcModel.Builder setA1(@FloatRange(from=-7.45E-9F, to=7.45E-9f) double);
+ method @NonNull public android.location.UtcModel.Builder setTimeOfWeek(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.UtcModel.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
}
package android.location.provider {
diff --git a/location/java/android/location/BeidouAssistance.java b/location/java/android/location/BeidouAssistance.java
new file mode 100644
index 000000000000..f55249e605a0
--- /dev/null
+++ b/location/java/android/location/BeidouAssistance.java
@@ -0,0 +1,283 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Beidou assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class BeidouAssistance implements Parcelable {
+
+ /** The Beidou almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Beidou ephemeris. */
+ @NonNull private final List<BeidouSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of Beidou satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private BeidouAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Beidou almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list ofBeidou ephemeris. */
+ @NonNull
+ public List<BeidouSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of Beidou satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ public static final @android.annotation.NonNull Creator<BeidouAssistance> CREATOR =
+ new Creator<BeidouAssistance>() {
+ @Override
+ public BeidouAssistance createFromParcel(Parcel in) {
+ return new BeidouAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(BeidouSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public BeidouAssistance[] newArray(int size) {
+ return new BeidouAssistance[size];
+ }
+ };
+
+ /** Builder for {@link BeidouAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<BeidouSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Beidou almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Beidou ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<BeidouSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of Beidou satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link BeidouAssistance}. */
+ @NonNull
+ public BeidouAssistance build() {
+ return new BeidouAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java
new file mode 100644
index 000000000000..6bd91e3318c3
--- /dev/null
+++ b/location/java/android/location/BeidouSatelliteEphemeris.java
@@ -0,0 +1,647 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to Beidou satellites.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class BeidouSatelliteEphemeris implements Parcelable {
+ /** The PRN number of the Beidou satellite. */
+ private final int mPrn;
+
+ /** Satellite clock model. */
+ private final BeidouSatelliteClockModel mSatelliteClockModel;
+
+ /** Satellite orbit model. */
+ private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ private final BeidouSatelliteHealth mSatelliteHealth;
+
+ /** Satellite ephemeris time. */
+ private final BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private BeidouSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 1, to = 63)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the satellite clock model. */
+ @NonNull
+ public BeidouSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public BeidouSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the satellite ephemeris time. */
+ @NonNull
+ public BeidouSatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteEphemeris> CREATOR =
+ new Creator<BeidouSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteEphemeris createFromParcel(Parcel in) {
+ final BeidouSatelliteEphemeris.Builder beidouSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setSatelliteClockModel(
+ in.readTypedObject(BeidouSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(BeidouSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(
+ BeidouSatelliteEphemerisTime.CREATOR));
+ return beidouSatelliteEphemeris.build();
+ }
+
+ @Override
+ public BeidouSatelliteEphemeris[] newArray(int size) {
+ return new BeidouSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteEphemeris[");
+ builder.append("prn = ").append(mPrn);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteEphemeris} */
+ public static final class Builder {
+ private int mPrn;
+ private BeidouSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private BeidouSatelliteHealth mSatelliteHealth;
+ private BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModel(
+ @NonNull BeidouSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull BeidouSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the satellite ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull BeidouSatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteEphemeris build() {
+ return new BeidouSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Beidou satellite clock correction.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0, section 5.2.4.9, 5.2.4.10.
+ */
+ public static final class BeidouSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds since Beidou epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Beidou
+ * navigation message in RINEX 3.05 Table A14.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias in seconds. */
+ private final double mAf0;
+
+ /** SV clock drift in seconds per second. */
+ private final double mAf1;
+
+ /** SV clock drift in seconds per second squared. */
+ private final double mAf2;
+
+ /** Group delay differential 1 B1/B3 in seconds. */
+ private final double mTgd1;
+
+ /** Group delay differential 2 B2/B3 in seconds. */
+ private final double mTgd2;
+
+ /**
+ * Age of Data Clock.
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 Section 5.2.4.8 Table 5-6.
+ */
+ private final int mAodc;
+
+ private BeidouSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -9.77e-3f, 9.77e-3f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.87e-9f, 1.87e-9f, "Af1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -1.39e-17f, 1.39e-17f, "Af2");
+ Preconditions.checkArgumentInRange(builder.mTgd1, -5.12e-8f, 5.12e-8f, "Tgd1");
+ Preconditions.checkArgumentInRange(builder.mTgd2, -5.12e-8f, 5.12e-8f, "Tgd2");
+ Preconditions.checkArgumentInRange(builder.mAodc, 0, 31, "Aodc");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mTgd1 = builder.mTgd1;
+ mTgd2 = builder.mTgd2;
+ mAodc = builder.mAodc;
+ }
+
+ /** Returns the time of the clock in seconds since Beidou epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias in seconds. */
+ @FloatRange(from = -9.77e-3f, to = 9.77e-3f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift in seconds per second. */
+ @FloatRange(from = -1.87e-9f, to = 1.87e-9f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the SV clock drift in seconds per second squared. */
+ @FloatRange(from = -1.39e-17f, to = 1.39e-17f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the group delay differential 1 B1/B3 in seconds. */
+ @FloatRange(from = -5.12e-8f, to = 5.12e-8f)
+ public double getTgd1() {
+ return mTgd1;
+ }
+
+ /** Returns the group delay differential 2 B2/B3 in seconds. */
+ @FloatRange(from = -5.12e-8f, to = 5.12e-8f)
+ public double getTgd2() {
+ return mTgd2;
+ }
+
+ /** Returns the age of data clock. */
+ @IntRange(from = 0, to = 31)
+ public int getAodc() {
+ return mAodc;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteClockModel> CREATOR =
+ new Creator<BeidouSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteClockModel createFromParcel(Parcel in) {
+ final BeidouSatelliteClockModel.Builder beidouSatelliteClockModel =
+ new Builder()
+ .setTimeOfClockSeconds(in.readLong())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble())
+ .setAf2(in.readDouble())
+ .setTgd1(in.readDouble())
+ .setTgd2(in.readDouble())
+ .setAodc(in.readInt());
+ return beidouSatelliteClockModel.build();
+ }
+
+ @Override
+ public BeidouSatelliteClockModel[] newArray(int size) {
+ return new BeidouSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mTgd1);
+ parcel.writeDouble(mTgd2);
+ parcel.writeInt(mAodc);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteClockModel[");
+ builder.append("timeOfClockSeonds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", tgd1 = ").append(mTgd1);
+ builder.append(", tgd2 = ").append(mTgd2);
+ builder.append(", aodc = ").append(mAodc);
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteClockModel} */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mTgd1;
+ private double mTgd2;
+ private int mAodc;
+
+ /** Sets the time of the clock in seconds since Beidou epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -9.77e-3f, to = 9.77e-3f)double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.87e-9f, to = 1.87e-9f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second squared. */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -1.39e-17f, to = 1.39e-17f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the group delay differential 1 B1/B3 in seconds. */
+ @NonNull
+ public Builder setTgd1(@FloatRange(from = -5.12e-8f, to = 5.12e-8f) double tgd1) {
+ mTgd1 = tgd1;
+ return this;
+ }
+
+ /** Sets the group delay differential 2 B2/B3 in seconds. */
+ @NonNull
+ public Builder setTgd2(@FloatRange(from = -5.12e-8f, to = 5.12e-8f) double tgd2) {
+ mTgd2 = tgd2;
+ return this;
+ }
+
+ /** Sets the age of data clock. */
+ @NonNull
+ public Builder setAodc(@IntRange(from = 0, to = 31) int aodc) {
+ mAodc = aodc;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteClockModel} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteClockModel build() {
+ return new BeidouSatelliteClockModel(this);
+ }
+ }
+ }
+
+ /** A class contains Beidou satellite health. */
+ public static final class BeidouSatelliteHealth implements Parcelable {
+ /**
+ * The autonomous satellite health flag (SatH1) occupies 1 bit.
+ *
+ * <p>“0” means broadcasting satellite is good and “1” means not.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.6.
+ */
+ private final int mSatH1;
+
+ /**
+ * SV accuracy in meters.
+ *
+ * <p>This is defined in the "BROADCAST ORBIT - 6" record of RINEX 3.05
+ * Table A14, pp.78.
+ */
+ private final double mSvAccur;
+
+ private BeidouSatelliteHealth(Builder builder) {
+ // Allow SatH1 beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSatH1 >= 0);
+ Preconditions.checkArgumentInRange(builder.mSvAccur, 0.0f, 8192.0f, "SvAccur");
+ mSatH1 = builder.mSatH1;
+ mSvAccur = builder.mSvAccur;
+ }
+
+ /** Returns the autonomous satellite health flag (SatH1) */
+ @IntRange(from = 0, to = 1)
+ public int getSatH1() {
+ return mSatH1;
+ }
+
+ /** Returns the SV accuracy in meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getSvAccur() {
+ return mSvAccur;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteHealth> CREATOR =
+ new Creator<BeidouSatelliteHealth>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteHealth createFromParcel(Parcel in) {
+ final BeidouSatelliteHealth.Builder beidouSatelliteHealth =
+ new Builder().setSatH1(in.readInt()).setSvAccur(in.readDouble());
+ return beidouSatelliteHealth.build();
+ }
+
+ @Override
+ public BeidouSatelliteHealth[] newArray(int size) {
+ return new BeidouSatelliteHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSatH1);
+ parcel.writeDouble(mSvAccur);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteHealth[");
+ builder.append("satH1 = ").append(mSatH1);
+ builder.append(", svAccur = ").append(mSvAccur);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteHealth} */
+ public static final class Builder {
+ private int mSatH1;
+ private double mSvAccur;
+
+ /** Sets the autonomous satellite health flag (SatH1) */
+ @NonNull
+ public Builder setSatH1(int satH1) {
+ mSatH1 = satH1;
+ return this;
+ }
+
+ /** Sets the SV accuracy in meters. */
+ @NonNull
+ public Builder setSvAccur(double svAccur) {
+ mSvAccur = svAccur;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteHealth} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteHealth build() {
+ return new BeidouSatelliteHealth(this);
+ }
+ }
+ }
+
+ /** A class contains Beidou satellite ephemeris time. */
+ public static final class BeidouSatelliteEphemerisTime implements Parcelable {
+ /**
+ * AODE Age of Data, Ephemeris.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.11 Table 5-8.
+ */
+ private final int mIode;
+
+ /** Beidou week number without rollover */
+ private final int mBeidouWeekNumber;
+
+ /**
+ * Time of ephemeris in seconds.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.12.
+ */
+ private final int mToeSeconds;
+
+ private BeidouSatelliteEphemerisTime(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mIode, 0, 31, "Iode");
+ Preconditions.checkArgument(builder.mBeidouWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604792, "ToeSeconds");
+ mIode = builder.mIode;
+ mBeidouWeekNumber = builder.mBeidouWeekNumber;
+ mToeSeconds = builder.mToeSeconds;
+ }
+
+ /** Returns the AODE Age of Data, Ephemeris. */
+ @IntRange(from = 0, to = 31)
+ public int getIode() {
+ return mIode;
+ }
+
+ /** Returns the Beidou week number without rollover . */
+ @IntRange(from = 0)
+ public int getBeidouWeekNumber() {
+ return mBeidouWeekNumber;
+ }
+
+ /** Returns the time of ephemeris in seconds. */
+ @IntRange(from = 0, to = 604792)
+ public int getToeSeconds() {
+ return mToeSeconds;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteEphemerisTime> CREATOR =
+ new Creator<BeidouSatelliteEphemerisTime>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteEphemerisTime createFromParcel(Parcel in) {
+ final BeidouSatelliteEphemerisTime.Builder beidouSatelliteEphemerisTime =
+ new Builder()
+ .setIode(in.readInt())
+ .setBeidouWeekNumber(in.readInt())
+ .setToeSeconds(in.readInt());
+ return beidouSatelliteEphemerisTime.build();
+ }
+
+ @Override
+ public BeidouSatelliteEphemerisTime[] newArray(int size) {
+ return new BeidouSatelliteEphemerisTime[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mIode);
+ parcel.writeInt(mBeidouWeekNumber);
+ parcel.writeInt(mToeSeconds);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteEphemerisTime[");
+ builder.append("iode = ").append(mIode);
+ builder.append(", beidouWeekNumber = ").append(mBeidouWeekNumber);
+ builder.append(", toeSeconds = ").append(mToeSeconds);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteEphemerisTime} */
+ public static final class Builder {
+ private int mIode;
+ private int mBeidouWeekNumber;
+ private int mToeSeconds;
+
+ /** Sets the AODE Age of Data, Ephemeris. */
+ @NonNull
+ public Builder setIode(int iode) {
+ mIode = iode;
+ return this;
+ }
+
+ /** Sets the Beidou week number without rollover */
+ @NonNull
+ public Builder setBeidouWeekNumber(
+ @IntRange(from = 0) int beidouWeekNumber) {
+ mBeidouWeekNumber = beidouWeekNumber;
+ return this;
+ }
+
+ /** Sets the time of ephemeris in seconds. */
+ @NonNull
+ public Builder setToeSeconds(@IntRange(from = 0, to = 604792) int toeSeconds) {
+ mToeSeconds = toeSeconds;
+ return this;
+ }
+
+ /**
+ * Builds a {@link BeidouSatelliteEphemerisTime} instance as specified by this builder.
+ */
+ @NonNull
+ public BeidouSatelliteEphemerisTime build() {
+ return new BeidouSatelliteEphemerisTime(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java
new file mode 100644
index 000000000000..07c5bab856db
--- /dev/null
+++ b/location/java/android/location/GalileoAssistance.java
@@ -0,0 +1,283 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Galileo assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoAssistance implements Parcelable {
+
+ /** The Galileo almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Galileo ephemeris. */
+ @NonNull private final List<GalileoSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of Galileo satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GalileoAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Galileo almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of Galileo ephemeris. */
+ @NonNull
+ public List<GalileoSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of Galileo satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public static final @android.annotation.NonNull Creator<GalileoAssistance> CREATOR =
+ new Creator<GalileoAssistance>() {
+ @Override
+ public GalileoAssistance createFromParcel(Parcel in) {
+ return new GalileoAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GalileoSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GalileoAssistance[] newArray(int size) {
+ return new GalileoAssistance[size];
+ }
+ };
+
+ /** Builder for {@link GalileoAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<GalileoSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Galileo almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Galileo ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GalileoSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of Galileo satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link GalileoAssistance}. */
+ @NonNull
+ public GalileoAssistance build() {
+ return new GalileoAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoIonosphericModel.java b/location/java/android/location/GalileoIonosphericModel.java
new file mode 100644
index 000000000000..6638be9af2b6
--- /dev/null
+++ b/location/java/android/location/GalileoIonosphericModel.java
@@ -0,0 +1,148 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains Galileo ionospheric model.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD-v2.1, section 5.1.6.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoIonosphericModel implements Parcelable {
+ /** Effective ionisation level 1st order parameter in sfu. */
+ private final double mAi0;
+
+ /** Effective ionisation level 2nd order parameter in sfu per degree. */
+ private final double mAi1;
+
+ /** Effective ionisation level 3nd order parameter in sfu per degree squared. */
+ private final double mAi2;
+
+ private GalileoIonosphericModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mAi0, 0.0f, 512.0f, "Ai0");
+ Preconditions.checkArgumentInRange(builder.mAi1, -4.0f, 4.0f, "Ai1");
+ Preconditions.checkArgumentInRange(builder.mAi2, -0.5f, 0.5f, "Ai2");
+ mAi0 = builder.mAi0;
+ mAi1 = builder.mAi1;
+ mAi2 = builder.mAi2;
+ }
+
+ /** Returns the effective ionisation level 1st order parameter in sfu. */
+ @FloatRange(from = 0.0f, to = 512.0f)
+ public double getAi0() {
+ return mAi0;
+ }
+
+ /** Returns the effective ionisation level 2nd order parameter in sfu per degree. */
+ @FloatRange(from = -4.0f, to = 4.0f)
+ public double getAi1() {
+ return mAi1;
+ }
+
+ /** Returns the effective ionisation level 3nd order parameter in sfu per degree squared. */
+ @FloatRange(from = -0.5f, to = 0.5f)
+ public double getAi2() {
+ return mAi2;
+ }
+
+ public static final @NonNull Parcelable.Creator<GalileoIonosphericModel> CREATOR =
+ new Parcelable.Creator<GalileoIonosphericModel>() {
+ @Override
+ public GalileoIonosphericModel createFromParcel(@NonNull Parcel source) {
+ return new GalileoIonosphericModel.Builder()
+ .setAi0(source.readDouble())
+ .setAi1(source.readDouble())
+ .setAi2(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GalileoIonosphericModel[] newArray(int size) {
+ return new GalileoIonosphericModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mAi0);
+ dest.writeDouble(mAi1);
+ dest.writeDouble(mAi2);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoIonosphericModel[");
+ builder.append("ai0 = ").append(mAi0);
+ builder.append(", ai1 = ").append(mAi1);
+ builder.append(", ai2 = ").append(mAi2);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoIonosphericModel}. */
+ public static final class Builder {
+ private double mAi0;
+ private double mAi1;
+ private double mAi2;
+
+ /** Sets the effective ionisation level 1st order parameter in sfu. */
+ @NonNull
+ public Builder setAi0(@FloatRange(from = 0.0f, to = 512.0f) double ai0) {
+ mAi0 = ai0;
+ return this;
+ }
+
+ /** Sets the effective ionisation level 2nd order parameter in sfu per degree. */
+ @NonNull
+ public Builder setAi1(@FloatRange(from = -4.0f, to = 4.0f) double ai1) {
+ mAi1 = ai1;
+ return this;
+ }
+
+ /** Sets the effective ionisation level 3nd order parameter in sfu per degree squared. */
+ @NonNull
+ public Builder setAi2(@FloatRange(from = -0.5f, to = 0.5f) double ai2) {
+ mAi2 = ai2;
+ return this;
+ }
+
+ /** Builds a {@link GalileoIonosphericModel}. */
+ @NonNull
+ public GalileoIonosphericModel build() {
+ return new GalileoIonosphericModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoSatelliteEphemeris.java b/location/java/android/location/GalileoSatelliteEphemeris.java
new file mode 100644
index 000000000000..7dd371176267
--- /dev/null
+++ b/location/java/android/location/GalileoSatelliteEphemeris.java
@@ -0,0 +1,660 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains ephemeris parameters specific to Galileo satellites.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoSatelliteEphemeris implements Parcelable {
+
+ /** Satellite code number. */
+ private int mSatelliteCodeNumber;
+
+ /** Array of satellite clock model. */
+ @NonNull private final List<GalileoSatelliteClockModel> mSatelliteClockModels;
+
+ /** Satellite orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GalileoSvHealth mSatelliteHealth;
+
+ /** Satellite ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private GalileoSatelliteEphemeris(Builder builder) {
+ // Allow satelliteCodeNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSatelliteCodeNumber >= 1);
+ Preconditions.checkNotNull(
+ builder.mSatelliteClockModels, "SatelliteClockModels cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteOrbitModel, "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth, "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteEphemerisTime, "SatelliteEphemerisTime cannot be null");
+ mSatelliteCodeNumber = builder.mSatelliteCodeNumber;
+ final List<GalileoSatelliteClockModel> satelliteClockModels = builder.mSatelliteClockModels;
+ mSatelliteClockModels = Collections.unmodifiableList(new ArrayList<>(satelliteClockModels));
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the satellite code number. */
+ @IntRange(from = 1, to = 36)
+ public int getSatelliteCodeNumber() {
+ return mSatelliteCodeNumber;
+ }
+
+ /** Returns the list of satellite clock models. */
+ @NonNull
+ public List<GalileoSatelliteClockModel> getSatelliteClockModels() {
+ return mSatelliteClockModels;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GalileoSvHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the satellite ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<GalileoSatelliteEphemeris> CREATOR =
+ new Creator<GalileoSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public GalileoSatelliteEphemeris createFromParcel(Parcel in) {
+ final GalileoSatelliteEphemeris.Builder galileoSatelliteEphemeris =
+ new Builder();
+ galileoSatelliteEphemeris.setSatelliteCodeNumber(in.readInt());
+ List<GalileoSatelliteClockModel> satelliteClockModels = new ArrayList<>();
+ in.readTypedList(satelliteClockModels, GalileoSatelliteClockModel.CREATOR);
+ galileoSatelliteEphemeris.setSatelliteClockModels(satelliteClockModels);
+ galileoSatelliteEphemeris.setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR));
+ galileoSatelliteEphemeris.setSatelliteHealth(
+ in.readTypedObject(GalileoSvHealth.CREATOR));
+ galileoSatelliteEphemeris.setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return galileoSatelliteEphemeris.build();
+ }
+
+ @Override
+ public GalileoSatelliteEphemeris[] newArray(int size) {
+ return new GalileoSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSatelliteCodeNumber);
+ parcel.writeTypedList(mSatelliteClockModels, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSatelliteEphemeris[");
+ builder.append("satelliteCodeNumber = ").append(mSatelliteCodeNumber);
+ builder.append(", satelliteClockModels = ").append(mSatelliteClockModels);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mSatelliteCodeNumber;
+ private List<GalileoSatelliteClockModel> mSatelliteClockModels;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GalileoSvHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the satellite code number. */
+ @NonNull
+ public Builder setSatelliteCodeNumber(
+ @IntRange(from = 1, to = 36) int satelliteCodeNumber) {
+ mSatelliteCodeNumber = satelliteCodeNumber;
+ return this;
+ }
+
+ /** Sets the array of satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModels(
+ @NonNull List<GalileoSatelliteClockModel> satelliteClockModels) {
+ mSatelliteClockModels = satelliteClockModels;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GalileoSvHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the satellite ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link GalileoSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public GalileoSatelliteEphemeris build() {
+ return new GalileoSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Galileo satellite health.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.9.3.
+ */
+ public static final class GalileoSvHealth implements Parcelable {
+ /** E1-B data validity status. */
+ private int mDataValidityStatusE1b;
+
+ /** E1-B/C signal health status. */
+ private int mSignalHealthStatusE1b;
+
+ /** E5a data validity status. */
+ private int mDataValidityStatusE5a;
+
+ /** E5a signal health status. */
+ private int mSignalHealthStatusE5a;
+
+ /** E5b data validity status. */
+ private int mDataValidityStatusE5b;
+
+ /** E5b signal health status. */
+ private int mSignalHealthStatusE5b;
+
+ private GalileoSvHealth(Builder builder) {
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE1b, 0, 1, "DataValidityStatusE1b");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE1b, 0, 3, "SignalHealthStatusE1b");
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE5a, 0, 1, "DataValidityStatusE5a");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE5a, 0, 3, "SignalHealthStatusE5a");
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE5b, 0, 1, "DataValidityStatusE5b");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE5b, 0, 3, "SignalHealthStatusE5b");
+ mDataValidityStatusE1b = builder.mDataValidityStatusE1b;
+ mSignalHealthStatusE1b = builder.mSignalHealthStatusE1b;
+ mDataValidityStatusE5a = builder.mDataValidityStatusE5a;
+ mSignalHealthStatusE5a = builder.mSignalHealthStatusE5a;
+ mDataValidityStatusE5b = builder.mDataValidityStatusE5b;
+ mSignalHealthStatusE5b = builder.mSignalHealthStatusE5b;
+ }
+
+ /** Returns the E1-B data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE1b() {
+ return mDataValidityStatusE1b;
+ }
+
+ /** Returns the E1-B/C signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE1b() {
+ return mSignalHealthStatusE1b;
+ }
+
+ /** Returns the E5a data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE5a() {
+ return mDataValidityStatusE5a;
+ }
+
+ /** Returns the E5a signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE5a() {
+ return mSignalHealthStatusE5a;
+ }
+
+ /** Returns the E5b data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE5b() {
+ return mDataValidityStatusE5b;
+ }
+
+ /** Returns the E5b signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE5b() {
+ return mSignalHealthStatusE5b;
+ }
+
+ public static final @NonNull Creator<GalileoSvHealth> CREATOR =
+ new Creator<GalileoSvHealth>() {
+ @Override
+ @NonNull
+ public GalileoSvHealth createFromParcel(Parcel in) {
+ final GalileoSvHealth.Builder galileoSvHealth = new Builder();
+ galileoSvHealth.setDataValidityStatusE1b(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE1b(in.readInt());
+ galileoSvHealth.setDataValidityStatusE5a(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE5a(in.readInt());
+ galileoSvHealth.setDataValidityStatusE5b(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE5b(in.readInt());
+ return galileoSvHealth.build();
+ }
+
+ @Override
+ public GalileoSvHealth[] newArray(int size) {
+ return new GalileoSvHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mDataValidityStatusE1b);
+ parcel.writeInt(mSignalHealthStatusE1b);
+ parcel.writeInt(mDataValidityStatusE5a);
+ parcel.writeInt(mSignalHealthStatusE5a);
+ parcel.writeInt(mDataValidityStatusE5b);
+ parcel.writeInt(mSignalHealthStatusE5b);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSvHealth[");
+ builder.append("dataValidityStatusE1b = ").append(mDataValidityStatusE1b);
+ builder.append(", signalHealthStatusE1b = ").append(mSignalHealthStatusE1b);
+ builder.append(", dataValidityStatusE5a = ").append(mDataValidityStatusE5a);
+ builder.append(", signalHealthStatusE5a = ").append(mSignalHealthStatusE5a);
+ builder.append(", dataValidityStatusE5b = ").append(mDataValidityStatusE5b);
+ builder.append(", signalHealthStatusE5b = ").append(mSignalHealthStatusE5b);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSvHealth}. */
+ public static final class Builder {
+ private int mDataValidityStatusE1b;
+ private int mSignalHealthStatusE1b;
+ private int mDataValidityStatusE5a;
+ private int mSignalHealthStatusE5a;
+ private int mDataValidityStatusE5b;
+ private int mSignalHealthStatusE5b;
+
+ /** Sets the E1-B data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE1b(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE1b) {
+ mDataValidityStatusE1b = dataValidityStatusE1b;
+ return this;
+ }
+
+ /** Sets the E1-B/C signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE1b(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE1b) {
+ mSignalHealthStatusE1b = signalHealthStatusE1b;
+ return this;
+ }
+
+ /** Sets the E5a data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE5a(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE5a) {
+ mDataValidityStatusE5a = dataValidityStatusE5a;
+ return this;
+ }
+
+ /** Sets the E5a signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE5a(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE5a) {
+ mSignalHealthStatusE5a = signalHealthStatusE5a;
+ return this;
+ }
+
+ /** Sets the E5b data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE5b(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE5b) {
+ mDataValidityStatusE5b = dataValidityStatusE5b;
+ return this;
+ }
+
+ /** Sets the E5b signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE5b(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE5b) {
+ mSignalHealthStatusE5b = signalHealthStatusE5b;
+ return this;
+ }
+
+ /** Builds a {@link GalileoSvHealth}. */
+ @NonNull
+ public GalileoSvHealth build() {
+ return new GalileoSvHealth(this);
+ }
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Galileo satellite clock correction.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.3.
+ */
+ public static final class GalileoSatelliteClockModel implements Parcelable {
+
+ /**
+ * The type of the satellite clock.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_UNDEFINED, TYPE_FNAV, TYPE_INAV})
+ public @interface SatelliteClockType {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * GalileoSatelliteEphemeris.aidl.
+ */
+
+ /** The type of the satellite clock is unknown. */
+ public static final int TYPE_UNDEFINED = 0;
+
+ /** The type of the satellite clock is FNAV. */
+ public static final int TYPE_FNAV = 1;
+
+ /** The type of the satellite clock is INAV. */
+ public static final int TYPE_INAV = 2;
+
+ /**
+ * Time of the clock in seconds since Galileo epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Galileo
+ * navigation message in RINEX 3.05 Table A8.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias correction coefficient in seconds. */
+ private double mAf0;
+
+ /** SV clock drift correction coefficient in seconds per second. */
+ private double mAf1;
+
+ /** SV clock drift rate correction coefficient in seconds per second squared. */
+ private double mAf2;
+
+ /**
+ * Broadcast group delay in seconds.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.5.
+ */
+ private double mBgdSeconds;
+
+ /**
+ * Signal in space accuracy in meters.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.12.
+ */
+ private double mSisaMeters;
+
+ /** Type of satellite clock . */
+ private final @SatelliteClockType int mSatelliteClockType;
+
+ private GalileoSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -0.0625f, 0.0625f, "AF0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.5e-8f, 1.5e-8f, "AF1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -5.56e-17f, 5.56e-17f, "AF2");
+ Preconditions.checkArgumentInRange(
+ builder.mBgdSeconds, -1.2e-7f, 1.2e-7f, "BgdSeconds");
+ Preconditions.checkArgument(builder.mSisaMeters >= 0.0f);
+ Preconditions.checkArgumentInRange(
+ builder.mSatelliteClockType, TYPE_UNDEFINED, TYPE_INAV, "SatelliteClockType");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mBgdSeconds = builder.mBgdSeconds;
+ mSisaMeters = builder.mSisaMeters;
+ mSatelliteClockType = builder.mSatelliteClockType;
+ }
+
+ /** Returns the time of the clock in seconds since Galileo epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias correction coefficient in seconds. */
+ @FloatRange(from = -0.0625f, to = 0.0625f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift correction coefficient in seconds per second. */
+ @FloatRange(from = -1.5e-8f, to = 1.5e-8f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the SV clock drift rate correction coefficient in seconds per second squared. */
+ @FloatRange(from = -5.56e-17f, to = 5.56e-17f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the broadcast group delay in seconds. */
+ @FloatRange(from = -1.2e-7f, to = 1.2e-7f)
+ public double getBgdSeconds() {
+ return mBgdSeconds;
+ }
+
+ /** Returns the signal in space accuracy in meters. */
+ @FloatRange(from = 0.0f)
+ public double getSisaMeters() {
+ return mSisaMeters;
+ }
+
+ /** Returns the type of satellite clock. */
+ public @SatelliteClockType int getSatelliteClockType() {
+ return mSatelliteClockType;
+ }
+
+ public static final @NonNull Creator<GalileoSatelliteClockModel> CREATOR =
+ new Creator<GalileoSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public GalileoSatelliteClockModel createFromParcel(Parcel in) {
+ final GalileoSatelliteClockModel.Builder galileoSatelliteClockModel =
+ new Builder();
+ galileoSatelliteClockModel.setTimeOfClockSeconds(in.readLong());
+ galileoSatelliteClockModel.setAf0(in.readDouble());
+ galileoSatelliteClockModel.setAf1(in.readDouble());
+ galileoSatelliteClockModel.setAf2(in.readDouble());
+ galileoSatelliteClockModel.setBgdSeconds(in.readDouble());
+ galileoSatelliteClockModel.setSisaMeters(in.readDouble());
+ galileoSatelliteClockModel.setSatelliteClockType(in.readInt());
+ return galileoSatelliteClockModel.build();
+ }
+
+ @Override
+ public GalileoSatelliteClockModel[] newArray(int size) {
+ return new GalileoSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mBgdSeconds);
+ parcel.writeDouble(mSisaMeters);
+ parcel.writeInt(mSatelliteClockType);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", bgdSeconds = ").append(mBgdSeconds);
+ builder.append(", sisaMeters = ").append(mSisaMeters);
+ builder.append(", satelliteClockType = ").append(mSatelliteClockType);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mBgdSeconds;
+ private double mSisaMeters;
+ private @SatelliteClockType int mSatelliteClockType;
+
+ /** Sets the time of the clock in seconds since Galileo epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias correction coefficient in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -0.0625f, to = 0.0625f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift correction coefficient in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.5e-8f, to = 1.5e-8f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /**
+ * Sets the SV clock drift rate correction coefficient in seconds per second squared.
+ */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -5.56e-17f, to = 5.56e-17f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the broadcast group delay in seconds. */
+ @NonNull
+ public Builder setBgdSeconds(
+ @FloatRange(from = -1.2e-7f, to = 1.2e-7f) double bgdSeconds) {
+ mBgdSeconds = bgdSeconds;
+ return this;
+ }
+
+ /** Sets the signal in space accuracy in meters. */
+ @NonNull
+ public Builder setSisaMeters(@FloatRange(from = 0.0f) double sisaMeters) {
+ mSisaMeters = sisaMeters;
+ return this;
+ }
+
+ /** Sets the type of satellite clock. */
+ @NonNull
+ public Builder setSatelliteClockType(@SatelliteClockType int satelliteClockType) {
+ mSatelliteClockType = satelliteClockType;
+ return this;
+ }
+
+ /**
+ * Builds a {@link GalileoSatelliteClockModel} instance as specified by this builder.
+ */
+ @NonNull
+ public GalileoSatelliteClockModel build() {
+ return new GalileoSatelliteClockModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassAlmanac.java b/location/java/android/location/GlonassAlmanac.java
new file mode 100644
index 000000000000..861dc5d257c4
--- /dev/null
+++ b/location/java/android/location/GlonassAlmanac.java
@@ -0,0 +1,418 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Glonass almanac data.
+ *
+ * <p>This is defined in Glonass ICD v5.1 section 4.5.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassAlmanac implements Parcelable {
+
+ /** Almanac issue date in milliseconds (UTC) */
+ private final long mIssueDateMillis;
+
+ /** List of GlonassSatelliteAlmanacs. */
+ @NonNull private final List<GlonassSatelliteAlmanac> mSatelliteAlmanacs;
+
+ /**
+ * Constructor for GlonassAlmanac.
+ *
+ * @param issueDateMillis The almanac issue date in milliseconds (UTC).
+ * @param satelliteAlmanacs The list of GlonassSatelliteAlmanac.
+ */
+ public GlonassAlmanac(
+ @IntRange(from = 0) long issueDateMillis,
+ @NonNull List<GlonassSatelliteAlmanac> satelliteAlmanacs) {
+ Preconditions.checkArgument(issueDateMillis >= 0);
+ Preconditions.checkNotNull(satelliteAlmanacs, "satelliteAlmanacs cannot be null");
+ mIssueDateMillis = issueDateMillis;
+ mSatelliteAlmanacs = Collections.unmodifiableList(new ArrayList<>(satelliteAlmanacs));
+ }
+
+ /** Returns the almanac issue date in milliseconds (UTC). */
+ @IntRange(from = 0)
+ public long getIssueDateMillis() {
+ return mIssueDateMillis;
+ }
+
+ /** Returns the list of GlonassSatelliteAlmanacs. */
+ @NonNull
+ public List<GlonassSatelliteAlmanac> getSatelliteAlmanacs() {
+ return mSatelliteAlmanacs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mIssueDateMillis);
+ dest.writeTypedList(mSatelliteAlmanacs);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassAlmanac> CREATOR =
+ new Parcelable.Creator<GlonassAlmanac>() {
+ @Override
+ public GlonassAlmanac createFromParcel(@NonNull Parcel in) {
+ long issueDateMillis = in.readLong();
+ List<GlonassSatelliteAlmanac> satelliteAlmanacs = new ArrayList<>();
+ in.readTypedList(satelliteAlmanacs, GlonassSatelliteAlmanac.CREATOR);
+ return new GlonassAlmanac(issueDateMillis, satelliteAlmanacs);
+ }
+
+ @Override
+ public GlonassAlmanac[] newArray(int size) {
+ return new GlonassAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassAlmanac[");
+ builder.append("issueDateMillis = ").append(mIssueDateMillis);
+ builder.append(", satelliteAlmanacs = ").append(mSatelliteAlmanacs);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * A class contains Glonass satellite almanac data.
+ *
+ * <p>This is defined in Glonass ICD v5.1 section 4.5.
+ */
+ public static final class GlonassSatelliteAlmanac implements Parcelable {
+ /** Slot number. */
+ private final int mSlotNumber;
+
+ /** Satellite health information (0=healthy, 1=unhealthy). */
+ private final int mSvHealth;
+
+ /** Frequency channel number. */
+ private final int mFreqChannel;
+
+ /** Coarse value of satellite time correction to GLONASS time in seconds. */
+ private final double mTau;
+
+ /** Time of first ascending node passage of satellite in seconds. */
+ private final double mTLambda;
+
+ /** Longitude of the first ascending node in semi-circles. */
+ private final double mLambda;
+
+ /** Correction to the mean value of inclination in semi-circles. */
+ private final double mDeltaI;
+
+ /** Correction to the mean value of the draconian period in seconds per orbital period */
+ private final double mDeltaT;
+
+ /** Rate of change of draconian period in seconds per orbital period squared. */
+ private final double mDeltaTDot;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /** Argument of perigee in radians. */
+ private final double mOmega;
+
+ private GlonassSatelliteAlmanac(Builder builder) {
+ // Allow slotNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSlotNumber >= 1);
+ // Allow svHealth beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgumentInRange(builder.mFreqChannel, 0, 31, "FreqChannel");
+ Preconditions.checkArgumentInRange(builder.mTau, -1.9e-3f, 1.9e-3f, "Tau");
+ Preconditions.checkArgumentInRange(builder.mTLambda, 0.0f, 44100.0f, "TLambda");
+ Preconditions.checkArgumentInRange(builder.mLambda, -1.0f, 1.0f, "Lambda");
+ Preconditions.checkArgumentInRange(builder.mDeltaI, -0.067f, 0.067f, "DeltaI");
+ Preconditions.checkArgumentInRange(builder.mDeltaT, -3600.0f, 3600.0f, "DeltaT");
+ Preconditions.checkArgumentInRange(builder.mDeltaTDot, -0.004f, 0.004f, "DeltaTDot");
+ Preconditions.checkArgumentInRange(builder.mEccentricity, 0.0f, 0.03f, "Eccentricity");
+ Preconditions.checkArgumentInRange(builder.mOmega, -1.0f, 1.0f, "Omega");
+ mSlotNumber = builder.mSlotNumber;
+ mSvHealth = builder.mSvHealth;
+ mFreqChannel = builder.mFreqChannel;
+ mTau = builder.mTau;
+ mTLambda = builder.mTLambda;
+ mLambda = builder.mLambda;
+ mDeltaI = builder.mDeltaI;
+ mDeltaT = builder.mDeltaT;
+ mDeltaTDot = builder.mDeltaTDot;
+ mEccentricity = builder.mEccentricity;
+ mOmega = builder.mOmega;
+ }
+
+ /** Returns the slot number. */
+ @IntRange(from = 1, to = 25)
+ public int getSlotNumber() {
+ return mSlotNumber;
+ }
+
+ /** Returns the Satellite health information (0=healthy, 1=unhealthy). */
+ @IntRange(from = 0, to = 1)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the frequency channel number. */
+ @IntRange(from = 0, to = 31)
+ public int getFreqChannel() {
+ return mFreqChannel;
+ }
+
+ /** Returns the coarse value of satellite time correction to GLONASS time in seconds. */
+ @FloatRange(from = -1.9e-3f, to = 1.9e-3f)
+ public double getTau() {
+ return mTau;
+ }
+
+ /** Returns the time of first ascending node passage of satellite in seconds. */
+ @FloatRange(from = 0.0f, to = 44100.0f)
+ public double getTLambda() {
+ return mTLambda;
+ }
+
+ /** Returns the longitude of the first ascending node in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getLambda() {
+ return mLambda;
+ }
+
+ /** Returns the correction to the mean value of inclination in semi-circles. */
+ @FloatRange(from = -0.067f, to = 0.067f)
+ public double getDeltaI() {
+ return mDeltaI;
+ }
+
+ /**
+ * Returns the correction to the mean value of the draconian period in seconds per orbital
+ * period
+ */
+ @FloatRange(from = -3600.0f, to = 3600.0f)
+ public double getDeltaT() {
+ return mDeltaT;
+ }
+
+ /** Returns the rate of change of draconian period in seconds per orbital period squared. */
+ @FloatRange(from = -0.004f, to = 0.004f)
+ public double getDeltaTDot() {
+ return mDeltaTDot;
+ }
+
+ /** Returns the eccentricity. */
+ @FloatRange(from = 0.0f, to = 0.03f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /** Returns the argument of perigee in radians. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSlotNumber);
+ dest.writeInt(mSvHealth);
+ dest.writeInt(mFreqChannel);
+ dest.writeDouble(mTau);
+ dest.writeDouble(mTLambda);
+ dest.writeDouble(mLambda);
+ dest.writeDouble(mDeltaI);
+ dest.writeDouble(mDeltaT);
+ dest.writeDouble(mDeltaTDot);
+ dest.writeDouble(mEccentricity);
+ dest.writeDouble(mOmega);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteAlmanac> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteAlmanac>() {
+ @Override
+ public GlonassSatelliteAlmanac createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteAlmanac.Builder()
+ .setSlotNumber(source.readInt())
+ .setSvHealth(source.readInt())
+ .setFreqChannel(source.readInt())
+ .setTau(source.readDouble())
+ .setTLambda(source.readDouble())
+ .setLambda(source.readDouble())
+ .setDeltaI(source.readDouble())
+ .setDeltaT(source.readDouble())
+ .setDeltaTDot(source.readDouble())
+ .setEccentricity(source.readDouble())
+ .setOmega(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteAlmanac[] newArray(int size) {
+ return new GlonassSatelliteAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteAlmanac[");
+ builder.append("slotNumber = ").append(mSlotNumber);
+ builder.append(", svHealth = ").append(mSvHealth);
+ builder.append(", freqChannel = ").append(mFreqChannel);
+ builder.append(", tau = ").append(mTau);
+ builder.append(", tLambda = ").append(mTLambda);
+ builder.append(", lambda = ").append(mLambda);
+ builder.append(", deltaI = ").append(mDeltaI);
+ builder.append(", deltaT = ").append(mDeltaT);
+ builder.append(", deltaTDot = ").append(mDeltaTDot);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", omega = ").append(mOmega);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteAlmanac}. */
+ public static final class Builder {
+ private int mSlotNumber;
+ private int mSvHealth;
+ private int mFreqChannel;
+ private double mTau;
+ private double mTLambda;
+ private double mLambda;
+ private double mDeltaI;
+ private double mDeltaT;
+ private double mDeltaTDot;
+ private double mEccentricity;
+ private double mOmega;
+
+ /** Sets the slot number. */
+ @NonNull
+ public Builder setSlotNumber(@IntRange(from = 1, to = 25) int slotNumber) {
+ mSlotNumber = slotNumber;
+ return this;
+ }
+
+ /** Sets the Satellite health information (0=healthy, 1=unhealthy). */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0, to = 1) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the frequency channel number. */
+ @NonNull
+ public Builder setFreqChannel(@IntRange(from = 0, to = 31) int freqChannel) {
+ mFreqChannel = freqChannel;
+ return this;
+ }
+
+ /** Sets the coarse value of satellite time correction to GLONASS time in seconds. */
+ @NonNull
+ public Builder setTau(@FloatRange(from = -1.9e-3f, to = 1.9e-3f) double tau) {
+ mTau = tau;
+ return this;
+ }
+
+ /** Sets the time of first ascending node passage of satellite in seconds. */
+ @NonNull
+ public Builder setTLambda(@FloatRange(from = 0.0f, to = 44100.0f) double tLambda) {
+ mTLambda = tLambda;
+ return this;
+ }
+
+ /** Sets the longitude of the first ascending node in semi-circles. */
+ @NonNull
+ public Builder setLambda(@FloatRange(from = -1.0f, to = 1.0f) double lambda) {
+ mLambda = lambda;
+ return this;
+ }
+
+ /** Sets the correction to the mean value of inclination in semi-circles. */
+ @NonNull
+ public Builder setDeltaI(@FloatRange(from = -0.067f, to = 0.067f) double deltaI) {
+ mDeltaI = deltaI;
+ return this;
+ }
+
+ /**
+ * Sets the correction to the mean value of the draconian period in seconds per orbital
+ * period.
+ */
+ @NonNull
+ public Builder setDeltaT(@FloatRange(from = -3600.0f, to = 3600.0f) double deltaT) {
+ mDeltaT = deltaT;
+ return this;
+ }
+
+ /**
+ * Sets the rate of change of draconian period in seconds per orbital period squared.
+ */
+ @NonNull
+ public Builder setDeltaTDot(@FloatRange(from = -0.004f, to = 0.004f) double deltaTDot) {
+ mDeltaTDot = deltaTDot;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(
+ @FloatRange(from = 0.0f, to = 0.03f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /** Sets the argument of perigee in radians. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -1.0f, to = 1.0f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteAlmanac}. */
+ @NonNull
+ public GlonassSatelliteAlmanac build() {
+ return new GlonassSatelliteAlmanac(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassAssistance.java b/location/java/android/location/GlonassAssistance.java
new file mode 100644
index 000000000000..cc0820197d8d
--- /dev/null
+++ b/location/java/android/location/GlonassAssistance.java
@@ -0,0 +1,213 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Glonass assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassAssistance implements Parcelable {
+
+ /** The Glonass almanac. */
+ @Nullable private final GlonassAlmanac mAlmanac;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Glonass ephemeris. */
+ @NonNull private final List<GlonassSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of Glonass satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GlonassAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mUtcModel = builder.mUtcModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Glonass almanac. */
+ @Nullable
+ public GlonassAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of Glonass satellite ephemeris. */
+ @NonNull
+ public List<GlonassSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of Glonass satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public static final @NonNull Creator<GlonassAssistance> CREATOR =
+ new Creator<GlonassAssistance>() {
+ @Override
+ public GlonassAssistance createFromParcel(Parcel in) {
+ return new GlonassAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GlonassAlmanac.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GlonassSatelliteEphemeris.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GlonassAssistance[] newArray(int size) {
+ return new GlonassAssistance[size];
+ }
+ };
+
+ /** Builder for {@link GlonassAssistance}. */
+ public static final class Builder {
+ private GlonassAlmanac mAlmanac;
+ private UtcModel mUtcModel;
+ private List<TimeModel> mTimeModels;
+ private List<GlonassSatelliteEphemeris> mSatelliteEphemeris;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Glonass almanac. */
+ @NonNull
+ public Builder setAlmanac(
+ @Nullable @SuppressLint("NullableCollection") GlonassAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(
+ @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Glonass satellite ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GlonassSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of Glonass satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link GlonassAssistance}. */
+ @NonNull
+ public GlonassAssistance build() {
+ return new GlonassAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassSatelliteEphemeris.java b/location/java/android/location/GlonassSatelliteEphemeris.java
new file mode 100644
index 000000000000..77a6ebb50cfb
--- /dev/null
+++ b/location/java/android/location/GlonassSatelliteEphemeris.java
@@ -0,0 +1,623 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to Glonass satellites.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassSatelliteEphemeris implements Parcelable {
+
+ /** L1/Satellite system (R), satellite number (slot number in sat. constellation). */
+ private final int mSlotNumber;
+
+ /** Health state (0=healthy, 1=unhealthy). */
+ private final int mHealthState;
+
+ /** Message frame time in seconds of the UTC week (tk+nd*86400). */
+ private final double mFrameTimeSeconds;
+
+ /** Age of current information in days (E). */
+ private final int mAgeInDays;
+
+ /** Satellite clock model. */
+ @NonNull private final GlonassSatelliteClockModel mSatelliteClockModel;
+
+ /** Satellite orbit model. */
+ @NonNull private final GlonassSatelliteOrbitModel mSatelliteOrbitModel;
+
+ private GlonassSatelliteEphemeris(Builder builder) {
+ // Allow SlotNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSlotNumber >= 1);
+ // Allow HealthState beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mHealthState >= 0);
+ Preconditions.checkArgument(builder.mFrameTimeSeconds >= 0.0f);
+ Preconditions.checkArgumentInRange(builder.mAgeInDays, 0, 31, "AgeInDays");
+ Preconditions.checkNotNull(
+ builder.mSatelliteClockModel, "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteOrbitModel, "SatelliteOrbitModel cannot be null");
+ mSlotNumber = builder.mSlotNumber;
+ mHealthState = builder.mHealthState;
+ mFrameTimeSeconds = builder.mFrameTimeSeconds;
+ mAgeInDays = builder.mAgeInDays;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ }
+
+ /**
+ * Returns the L1/Satellite system (R), satellite number (slot number in sat. constellation).
+ */
+ @IntRange(from = 1, to = 25)
+ public int getSlotNumber() {
+ return mSlotNumber;
+ }
+
+ /** Returns the health state (0=healthy, 1=unhealthy). */
+ @IntRange(from = 0, to = 1)
+ public int getHealthState() {
+ return mHealthState;
+ }
+
+ /** Returns the message frame time in seconds of the UTC week (tk+nd*86400). */
+ @FloatRange(from = 0.0f)
+ public double getFrameTimeSeconds() {
+ return mFrameTimeSeconds;
+ }
+
+ /** Returns the age of current information in days (E). */
+ @IntRange(from = 0, to = 31)
+ public int getAgeInDays() {
+ return mAgeInDays;
+ }
+
+ /** Returns the satellite clock model. */
+ @NonNull
+ public GlonassSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public GlonassSatelliteOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSlotNumber);
+ dest.writeInt(mHealthState);
+ dest.writeDouble(mFrameTimeSeconds);
+ dest.writeInt(mAgeInDays);
+ dest.writeTypedObject(mSatelliteClockModel, flags);
+ dest.writeTypedObject(mSatelliteOrbitModel, flags);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteEphemeris> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteEphemeris>() {
+ @Override
+ public GlonassSatelliteEphemeris createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteEphemeris.Builder()
+ .setSlotNumber(source.readInt())
+ .setHealthState(source.readInt())
+ .setFrameTimeSeconds(source.readDouble())
+ .setAgeInDays(source.readInt())
+ .setSatelliteClockModel(
+ source.readTypedObject(GlonassSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ source.readTypedObject(GlonassSatelliteOrbitModel.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteEphemeris[] newArray(int size) {
+ return new GlonassSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteEphemeris[");
+ builder.append("slotNumber = ").append(mSlotNumber);
+ builder.append(", healthState = ").append(mHealthState);
+ builder.append(", frameTimeSeconds = ").append(mFrameTimeSeconds);
+ builder.append(", ageInDays = ").append(mAgeInDays);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mSlotNumber;
+ private int mHealthState;
+ private double mFrameTimeSeconds;
+ private int mAgeInDays;
+ private GlonassSatelliteClockModel mSatelliteClockModel;
+ private GlonassSatelliteOrbitModel mSatelliteOrbitModel;
+
+ /**
+ * Sets the L1/Satellite system (R), satellite number (slot number in sat. constellation).
+ */
+ @NonNull
+ public Builder setSlotNumber(@IntRange(from = 1, to = 25) int slotNumber) {
+ mSlotNumber = slotNumber;
+ return this;
+ }
+
+ /** Sets the health state (0=healthy, 1=unhealthy). */
+ @NonNull
+ public Builder setHealthState(@IntRange(from = 0, to = 1) int healthState) {
+ mHealthState = healthState;
+ return this;
+ }
+
+ /** Sets the message frame time in seconds of the UTC week (tk+nd*86400). */
+ @NonNull
+ public Builder setFrameTimeSeconds(@FloatRange(from = 0.0f) double frameTimeSeconds) {
+ mFrameTimeSeconds = frameTimeSeconds;
+ return this;
+ }
+
+ /** Sets the age of current information in days (E). */
+ @NonNull
+ public Builder setAgeInDays(@IntRange(from = 0, to = 31) int ageInDays) {
+ mAgeInDays = ageInDays;
+ return this;
+ }
+
+ /** Sets the satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModel(
+ @NonNull GlonassSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(
+ @NonNull GlonassSatelliteOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteEphemeris}. */
+ @NonNull
+ public GlonassSatelliteEphemeris build() {
+ return new GlonassSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Glonass satellite clock correction.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ */
+ public static final class GlonassSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds (UTC)
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Glonass
+ * navigation message in RINEX 3.05 Table A10.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** Clock bias in seconds (-TauN). */
+ private final double mClockBias;
+
+ /** Frequency bias (+GammaN). */
+ private final double mFrequencyBias;
+
+ /** Frequency number. */
+ private final int mFrequencyNumber;
+
+ private GlonassSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mClockBias, -0.002f, 0.002f, "ClockBias");
+ Preconditions.checkArgumentInRange(
+ builder.mFrequencyBias, -9.32e-10f, 9.32e-10f, "FrequencyBias");
+ Preconditions.checkArgumentInRange(builder.mFrequencyNumber, -7, 6, "FrequencyNumber");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mClockBias = builder.mClockBias;
+ mFrequencyBias = builder.mFrequencyBias;
+ mFrequencyNumber = builder.mFrequencyNumber;
+ }
+
+ /** Returns the time of clock in seconds (UTC). */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the clock bias in seconds (-TauN). */
+ @FloatRange(from = -0.002f, to = 0.002f)
+ public double getClockBias() {
+ return mClockBias;
+ }
+
+ /** Returns the frequency bias (+GammaN). */
+ @FloatRange(from = -9.32e-10f, to = 9.32e-10f)
+ public double getFrequencyBias() {
+ return mFrequencyBias;
+ }
+
+ /** Returns the frequency number. */
+ @IntRange(from = -7, to = 6)
+ public int getFrequencyNumber() {
+ return mFrequencyNumber;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mTimeOfClockSeconds);
+ dest.writeDouble(mClockBias);
+ dest.writeDouble(mFrequencyBias);
+ dest.writeInt(mFrequencyNumber);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteClockModel> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteClockModel>() {
+ @Override
+ public GlonassSatelliteClockModel createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteClockModel.Builder()
+ .setTimeOfClockSeconds(source.readLong())
+ .setClockBias(source.readDouble())
+ .setFrequencyBias(source.readDouble())
+ .setFrequencyNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteClockModel[] newArray(int size) {
+ return new GlonassSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", clockBias = ").append(mClockBias);
+ builder.append(", frequencyBias = ").append(mFrequencyBias);
+ builder.append(", frequencyNumber = ").append(mFrequencyNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mClockBias;
+ private double mFrequencyBias;
+ private int mFrequencyNumber;
+
+ /** Sets the time of clock in seconds (UTC). */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the clock bias in seconds (-TauN). */
+ @NonNull
+ public Builder setClockBias(@FloatRange(from = -0.002f, to = 0.002f) double clockBias) {
+ mClockBias = clockBias;
+ return this;
+ }
+
+ /** Sets the frequency bias (+GammaN). */
+ @NonNull
+ public Builder setFrequencyBias(
+ @FloatRange(from = -9.32e-10f, to = 9.32e-10f) double frequencyBias) {
+ mFrequencyBias = frequencyBias;
+ return this;
+ }
+
+ /** Sets the frequency number. */
+ @NonNull
+ public Builder setFrequencyNumber(@IntRange(from = -7, to = 6) int frequencyNumber) {
+ mFrequencyNumber = frequencyNumber;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteClockModel}. */
+ @NonNull
+ public GlonassSatelliteClockModel build() {
+ return new GlonassSatelliteClockModel(this);
+ }
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Glonass satellite orbit correction.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ */
+ public static final class GlonassSatelliteOrbitModel implements Parcelable {
+ /** X position in kilometers. */
+ private final double mX;
+
+ /** X velocity in kilometers per second. */
+ private final double mXDot;
+
+ /** X acceleration in kilometers per second squared. */
+ private final double mXAccel;
+
+ /** Y position in kilometers. */
+ private final double mY;
+
+ /** Y velocity in kilometers per second. */
+ private final double mYDot;
+
+ /** Y acceleration in kilometers per second squared. */
+ private final double mYAccel;
+
+ /** Z position in kilometers. */
+ private final double mZ;
+
+ /** Z velocity in kilometers per second. */
+ private final double mZDot;
+
+ /** Z acceleration in kilometers per second squared. */
+ private final double mZAccel;
+
+ private GlonassSatelliteOrbitModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mX, -2.7e4f, 2.7e4f, "X");
+ Preconditions.checkArgumentInRange(builder.mXDot, -4.3f, 4.3f, "XDot");
+ Preconditions.checkArgumentInRange(builder.mXAccel, -6.2e-9f, 6.2e-9f, "XAccel");
+ Preconditions.checkArgumentInRange(builder.mY, -2.7e4f, 2.7e4f, "Y");
+ Preconditions.checkArgumentInRange(builder.mYDot, -4.3f, 4.3f, "YDot");
+ Preconditions.checkArgumentInRange(builder.mYAccel, -6.2e-9f, 6.2e-9f, "YAccel");
+ Preconditions.checkArgumentInRange(builder.mZ, -2.7e4f, 2.7e4f, "Z");
+ Preconditions.checkArgumentInRange(builder.mZDot, -4.3f, 4.3f, "ZDot");
+ Preconditions.checkArgumentInRange(builder.mZAccel, -6.2e-9f, 6.2e-9f, "ZAccel");
+ mX = builder.mX;
+ mXDot = builder.mXDot;
+ mXAccel = builder.mXAccel;
+ mY = builder.mY;
+ mYDot = builder.mYDot;
+ mYAccel = builder.mYAccel;
+ mZ = builder.mZ;
+ mZDot = builder.mZDot;
+ mZAccel = builder.mZAccel;
+ }
+
+ /** Returns the X position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getX() {
+ return mX;
+ }
+
+ /** Returns the X velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getXDot() {
+ return mXDot;
+ }
+
+ /** Returns the X acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getXAccel() {
+ return mXAccel;
+ }
+
+ /** Returns the Y position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getY() {
+ return mY;
+ }
+
+ /** Returns the Y velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getYDot() {
+ return mYDot;
+ }
+
+ /** Returns the Y acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getYAccel() {
+ return mYAccel;
+ }
+
+ /** Returns the Z position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getZ() {
+ return mZ;
+ }
+
+ /** Returns the Z velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getZDot() {
+ return mZDot;
+ }
+
+ /** Returns the Z acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getZAccel() {
+ return mZAccel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mX);
+ dest.writeDouble(mXDot);
+ dest.writeDouble(mXAccel);
+ dest.writeDouble(mY);
+ dest.writeDouble(mYDot);
+ dest.writeDouble(mYAccel);
+ dest.writeDouble(mZ);
+ dest.writeDouble(mZDot);
+ dest.writeDouble(mZAccel);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteOrbitModel> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteOrbitModel>() {
+ @Override
+ public GlonassSatelliteOrbitModel createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteOrbitModel.Builder()
+ .setX(source.readDouble())
+ .setXDot(source.readDouble())
+ .setXAccel(source.readDouble())
+ .setY(source.readDouble())
+ .setYDot(source.readDouble())
+ .setYAccel(source.readDouble())
+ .setZ(source.readDouble())
+ .setZDot(source.readDouble())
+ .setZAccel(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteOrbitModel[] newArray(int size) {
+ return new GlonassSatelliteOrbitModel[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteOrbitModel[");
+ builder.append("x = ").append(mX);
+ builder.append(", xDot = ").append(mXDot);
+ builder.append(", xAccel = ").append(mXAccel);
+ builder.append(", y = ").append(mY);
+ builder.append(", yDot = ").append(mYDot);
+ builder.append(", yAccel = ").append(mYAccel);
+ builder.append(", z = ").append(mZ);
+ builder.append(", zDot = ").append(mZDot);
+ builder.append(", zAccel = ").append(mZAccel);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteOrbitModel}. */
+ public static final class Builder {
+ private double mX;
+ private double mXDot;
+ private double mXAccel;
+ private double mY;
+ private double mYDot;
+ private double mYAccel;
+ private double mZ;
+ private double mZDot;
+ private double mZAccel;
+
+ /** Sets the X position in kilometers. */
+ @NonNull
+ public Builder setX(@FloatRange(from = -2.7e4f, to = 2.7e4f) double x) {
+ mX = x;
+ return this;
+ }
+
+ /** Sets the X velocity in kilometers per second. */
+ @NonNull
+ public Builder setXDot(@FloatRange(from = -4.3f, to = 4.3f) double xDot) {
+ mXDot = xDot;
+ return this;
+ }
+
+ /** Sets the X acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setXAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double xAccel) {
+ mXAccel = xAccel;
+ return this;
+ }
+
+ /** Sets the Y position in kilometers. */
+ @NonNull
+ public Builder setY(@FloatRange(from = -2.7e4f, to = 2.7e4f) double y) {
+ mY = y;
+ return this;
+ }
+
+ /** Sets the Y velocity in kilometers per second. */
+ @NonNull
+ public Builder setYDot(@FloatRange(from = -4.3f, to = 4.3f) double yDot) {
+ mYDot = yDot;
+ return this;
+ }
+
+ /** Sets the Y acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setYAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double yAccel) {
+ mYAccel = yAccel;
+ return this;
+ }
+
+ /** Sets the Z position in kilometers. */
+ @NonNull
+ public Builder setZ(@FloatRange(from = -2.7e4f, to = 2.7e4f) double z) {
+ mZ = z;
+ return this;
+ }
+
+ /** Sets the Z velocity in kilometers per second. */
+ @NonNull
+ public Builder setZDot(@FloatRange(from = -4.3f, to = 4.3f) double zDot) {
+ mZDot = zDot;
+ return this;
+ }
+
+ /** Sets the Z acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setZAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double zAccel) {
+ mZAccel = zAccel;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteOrbitModel}. */
+ @NonNull
+ public GlonassSatelliteOrbitModel build() {
+ return new GlonassSatelliteOrbitModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GnssAlmanac.java b/location/java/android/location/GnssAlmanac.java
new file mode 100644
index 000000000000..6466e45a965e
--- /dev/null
+++ b/location/java/android/location/GnssAlmanac.java
@@ -0,0 +1,619 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains almanac parameters for GPS, QZSS, Galileo, Beidou.
+ *
+ * <p>For Beidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.15.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200 section 20.3.3.5.1.2.
+ *
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.6.
+ *
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssAlmanac implements Parcelable {
+ /**
+ * Almanac issue date in milliseconds (UTC).
+ *
+ * <p>This is unused for GPS/QZSS/Baidou.
+ */
+ private final long mIssueDateMillis;
+
+ /**
+ * Almanac issue of data.
+ *
+ * <p>This is unused for GPS/QZSS/Baidou.
+ */
+ private final int mIod;
+
+ /**
+ * Almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ private final int mWeekNumber;
+
+ /** Almanac reference time in seconds. */
+ private final int mToaSeconds;
+
+ /** The list of GnssSatelliteAlmanacs. */
+ @NonNull private final List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
+
+ private GnssAlmanac(Builder builder) {
+ Preconditions.checkArgument(builder.mIssueDateMillis >= 0);
+ Preconditions.checkArgument(builder.mIod >= 0);
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToaSeconds, 0, 604800, "ToaSeconds");
+ Preconditions.checkNotNull(
+ builder.mGnssSatelliteAlmanacs, "GnssSatelliteAlmanacs cannot be null");
+ mIssueDateMillis = builder.mIssueDateMillis;
+ mIod = builder.mIod;
+ mWeekNumber = builder.mWeekNumber;
+ mToaSeconds = builder.mToaSeconds;
+ mGnssSatelliteAlmanacs =
+ Collections.unmodifiableList(new ArrayList<>(builder.mGnssSatelliteAlmanacs));
+ }
+
+ /** Returns the almanac issue date in milliseconds (UTC). */
+ @IntRange(from = 0)
+ public long getIssueDateMillis() {
+ return mIssueDateMillis;
+ }
+
+ /** Returns the almanac issue of data. */
+ @IntRange(from = 0)
+ public int getIod() {
+ return mIod;
+ }
+
+ /**
+ * Returns the almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ /** Returns the almanac reference time in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getToaSeconds() {
+ return mToaSeconds;
+ }
+
+ /** Returns the list of GnssSatelliteAlmanacs. */
+ @NonNull
+ public List<GnssSatelliteAlmanac> getGnssSatelliteAlmanacs() {
+ return mGnssSatelliteAlmanacs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mIssueDateMillis);
+ dest.writeInt(mIod);
+ dest.writeInt(mWeekNumber);
+ dest.writeInt(mToaSeconds);
+ dest.writeTypedList(mGnssSatelliteAlmanacs);
+ }
+
+ public static final @NonNull Creator<GnssAlmanac> CREATOR =
+ new Creator<GnssAlmanac>() {
+ @Override
+ public GnssAlmanac createFromParcel(Parcel in) {
+ GnssAlmanac.Builder gnssAlmanac = new GnssAlmanac.Builder();
+ gnssAlmanac.setIssueDateMillis(in.readLong());
+ gnssAlmanac.setIod(in.readInt());
+ gnssAlmanac.setWeekNumber(in.readInt());
+ gnssAlmanac.setToaSeconds(in.readInt());
+ List<GnssSatelliteAlmanac> satelliteAlmanacs = new ArrayList<>();
+ in.readTypedList(satelliteAlmanacs, GnssSatelliteAlmanac.CREATOR);
+ gnssAlmanac.setGnssSatelliteAlmanacs(satelliteAlmanacs);
+ return gnssAlmanac.build();
+ }
+
+ @Override
+ public GnssAlmanac[] newArray(int size) {
+ return new GnssAlmanac[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAlmanac[");
+ builder.append("issueDateMillis=").append(mIssueDateMillis);
+ builder.append(", iod=").append(mIod);
+ builder.append(", weekNumber=").append(mWeekNumber);
+ builder.append(", toaSeconds=").append(mToaSeconds);
+ builder.append(", satelliteAlmanacs=").append(mGnssSatelliteAlmanacs);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssAlmanac}. */
+ public static final class Builder {
+ private long mIssueDateMillis;
+ private int mIod;
+ private int mWeekNumber;
+ private int mToaSeconds;
+ private List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
+
+ /** Sets the almanac issue date in milliseconds (UTC). */
+ @NonNull
+ public Builder setIssueDateMillis(@IntRange(from = 0) long issueDateMillis) {
+ mIssueDateMillis = issueDateMillis;
+ return this;
+ }
+
+ /** Sets the almanac issue of data. */
+ @NonNull
+ public Builder setIod(@IntRange(from = 0) int iod) {
+ mIod = iod;
+ return this;
+ }
+
+ /**
+ * Sets the almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Sets the almanac reference time in seconds. */
+ @NonNull
+ public Builder setToaSeconds(@IntRange(from = 0, to = 604800) int toaSeconds) {
+ mToaSeconds = toaSeconds;
+ return this;
+ }
+
+ /** Sets the list of GnssSatelliteAlmanacs. */
+ @NonNull
+ public Builder setGnssSatelliteAlmanacs(
+ @NonNull List<GnssSatelliteAlmanac> gnssSatelliteAlmanacs) {
+ mGnssSatelliteAlmanacs = gnssSatelliteAlmanacs;
+ return this;
+ }
+
+ /** Builds a {@link GnssAlmanac} instance as specified by this builder. */
+ @NonNull
+ public GnssAlmanac build() {
+ return new GnssAlmanac(this);
+ }
+ }
+
+ /**
+ * A class contains almanac parameters for GPS, QZSS, Galileo, Beidou.
+ *
+ * <p>For Beidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.15.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200 section 20.3.3.5.1.2.
+ *
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.6.
+ *
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
+ */
+ public static final class GnssSatelliteAlmanac implements Parcelable {
+ /** The PRN number of the GNSS satellite. */
+ private final int mSvid;
+
+ /**
+ * Satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined in
+ * IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a, bit 2 and 3 is for E5b, bit
+ * 4 and 5 is for E1b.
+ */
+ private final int mSvHealth;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /**
+ * Inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at reference
+ * time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in semi-circles.
+ */
+ private final double mInclination;
+
+ /** Argument of perigee in semi-circles. */
+ private final double mOmega;
+
+ /** Longitude of ascending node of orbital plane at weekly epoch in semi-circles. */
+ private final double mOmega0;
+
+ /** Rate of right ascension in semi-circles per second. */
+ private final double mOmegaDot;
+
+ /**
+ * Square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ private final double mRootA;
+
+ /** Mean anomaly at reference time in semi-circles. */
+ private final double mM0;
+
+ /** Satellite clock time bias correction coefficient in seconds. */
+ private final double mAf0;
+
+ /** Satellite clock time drift correction coefficient in seconds per second. */
+ private final double mAf1;
+
+ private GnssSatelliteAlmanac(Builder builder) {
+ Preconditions.checkArgument(builder.mSvid > 0);
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgument(builder.mEccentricity >= 0.0f);
+ Preconditions.checkArgumentInRange(builder.mInclination, -1.0f, 1.0f, "Inclination");
+ Preconditions.checkArgumentInRange(builder.mOmega, -1.0f, 1.0f, "Omega");
+ Preconditions.checkArgumentInRange(builder.mOmega0, -1.0f, 1.0f, "Omega0");
+ Preconditions.checkArgumentInRange(builder.mOmegaDot, -1.0f, 1.0f, "OmegaDot");
+ Preconditions.checkArgumentInRange(builder.mRootA, 0.0f, 8192.0f, "RootA");
+ Preconditions.checkArgumentInRange(builder.mM0, -1.0f, 1.0f, "M0");
+ Preconditions.checkArgumentInRange(builder.mAf0, -0.0625f, 0.0625f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.5e-8f, 1.5e-8f, "Af1");
+ mSvid = builder.mSvid;
+ mSvHealth = builder.mSvHealth;
+ mEccentricity = builder.mEccentricity;
+ mInclination = builder.mInclination;
+ mOmega = builder.mOmega;
+ mOmega0 = builder.mOmega0;
+ mOmegaDot = builder.mOmegaDot;
+ mRootA = builder.mRootA;
+ mM0 = builder.mM0;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ }
+
+ /** Returns the PRN number of the GNSS satellite. */
+ @IntRange(from = 1)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /**
+ * Returns the satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined in
+ * IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a, bit 2 and 3 is for E5b,
+ * bit 4 and 5 is for E1b.
+ */
+ @IntRange(from = 0)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the eccentricity. */
+ @FloatRange(from = 0.0f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /**
+ * Returns the inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at reference
+ * time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in semi-circles.
+ */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getInclination() {
+ return mInclination;
+ }
+
+ /** Returns the argument of perigee in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ /**
+ * Returns the longitude of ascending node of orbital plane at weekly epoch in semi-circles.
+ */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega0() {
+ return mOmega0;
+ }
+
+ /** Returns the rate of right ascension in semi-circles per second. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmegaDot() {
+ return mOmegaDot;
+ }
+
+ /**
+ * Returns the square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getRootA() {
+ return mRootA;
+ }
+
+ /** Returns the mean anomaly at reference time in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getM0() {
+ return mM0;
+ }
+
+ /** Returns the satellite clock time bias correction coefficient in seconds. */
+ @FloatRange(from = -0.0625f, to = 0.0625f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the satellite clock time drift correction coefficient in seconds per second. */
+ @FloatRange(from = -1.5e-8f, to = 1.5e-8f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSvid);
+ dest.writeInt(mSvHealth);
+ dest.writeDouble(mEccentricity);
+ dest.writeDouble(mInclination);
+ dest.writeDouble(mOmega);
+ dest.writeDouble(mOmega0);
+ dest.writeDouble(mOmegaDot);
+ dest.writeDouble(mRootA);
+ dest.writeDouble(mM0);
+ dest.writeDouble(mAf0);
+ dest.writeDouble(mAf1);
+ }
+
+ public static final @NonNull Creator<GnssSatelliteAlmanac> CREATOR =
+ new Creator<GnssSatelliteAlmanac>() {
+ @Override
+ public GnssSatelliteAlmanac createFromParcel(Parcel in) {
+ return new GnssSatelliteAlmanac(
+ new Builder()
+ .setSvid(in.readInt())
+ .setSvHealth(in.readInt())
+ .setEccentricity(in.readDouble())
+ .setInclination(in.readDouble())
+ .setOmega(in.readDouble())
+ .setOmega0(in.readDouble())
+ .setOmegaDot(in.readDouble())
+ .setRootA(in.readDouble())
+ .setM0(in.readDouble())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble()));
+ }
+
+ @Override
+ public GnssSatelliteAlmanac[] newArray(int size) {
+ return new GnssSatelliteAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssSatelliteAlmanac[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", svHealth = ").append(mSvHealth);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", inclination = ").append(mInclination);
+ builder.append(", omega = ").append(mOmega);
+ builder.append(", omega0 = ").append(mOmega0);
+ builder.append(", omegaDot = ").append(mOmegaDot);
+ builder.append(", rootA = ").append(mRootA);
+ builder.append(", m0 = ").append(mM0);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssSatelliteAlmanac}. */
+ public static final class Builder {
+ private int mSvid;
+ private int mSvHealth;
+ private double mEccentricity;
+ private double mInclination;
+ private double mOmega;
+ private double mOmega0;
+ private double mOmegaDot;
+ private double mRootA;
+ private double mM0;
+ private double mAf0;
+ private double mAf1;
+
+ /** Sets the PRN number of the GNSS satellite. */
+ @NonNull
+ public Builder setSvid(@IntRange(from = 1) int svid) {
+ mSvid = svid;
+ return this;
+ }
+
+ /**
+ * Sets the satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined
+ * in IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a,
+ * bit 2 and 3 is for E5b, bit 4 and 5 is for E1b.
+ */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(@FloatRange(from = 0.0f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /**
+ * Sets the inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at
+ * reference time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in
+ * semi-circles.
+ */
+ @NonNull
+ public Builder setInclination(@FloatRange(from = -1.0f, to = 1.0f) double inclination) {
+ mInclination = inclination;
+ return this;
+ }
+
+ /** Sets the argument of perigee in semi-circles. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -1.0f, to = 1.0f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of ascending node of orbital plane at weekly epoch in
+ * semi-circles.
+ */
+ @NonNull
+ public Builder setOmega0(@FloatRange(from = -1.0f, to = 1.0f) double omega0) {
+ mOmega0 = omega0;
+ return this;
+ }
+
+ /** Sets the rate of right ascension in semi-circles per second. */
+ @NonNull
+ public Builder setOmegaDot(@FloatRange(from = -1.0f, to = 1.0f) double omegaDot) {
+ mOmegaDot = omegaDot;
+ return this;
+ }
+
+ /**
+ * Sets the square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ @NonNull
+ public Builder setRootA(@FloatRange(from = 0.0f, to = 8192.0f) double rootA) {
+ mRootA = rootA;
+ return this;
+ }
+
+ /** Sets the mean anomaly at reference time in semi-circles. */
+ @NonNull
+ public Builder setM0(@FloatRange(from = -1.0f, to = 1.0f) double m0) {
+ mM0 = m0;
+ return this;
+ }
+
+ /** Sets the satellite clock time bias correction coefficient in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -0.0625f, to = 0.0625f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the satellite clock time drift correction coefficient in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.5e-8f, to = 1.5e-8f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Builds a {@link GnssSatelliteAlmanac} instance as specified by this builder. */
+ @NonNull
+ public GnssSatelliteAlmanac build() {
+ return new GnssSatelliteAlmanac(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GnssAssistance.aidl b/location/java/android/location/GnssAssistance.aidl
new file mode 100644
index 000000000000..2745683ec330
--- /dev/null
+++ b/location/java/android/location/GnssAssistance.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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 android.location;
+
+parcelable GnssAssistance; \ No newline at end of file
diff --git a/location/java/android/location/GnssAssistance.java b/location/java/android/location/GnssAssistance.java
new file mode 100644
index 000000000000..e941122f8c79
--- /dev/null
+++ b/location/java/android/location/GnssAssistance.java
@@ -0,0 +1,310 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains GNSS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssAssistance implements Parcelable {
+
+ /** GPS assistance. */
+ @Nullable private final GpsAssistance mGpsAssistance;
+
+ /** Glonass assistance. */
+ @Nullable private final GlonassAssistance mGlonassAssistance;
+
+ /** Galileo assistance. */
+ @Nullable private final GalileoAssistance mGalileoAssistance;
+
+ /** Beidou assistance. */
+ @Nullable private final BeidouAssistance mBeidouAssistance;
+
+ /** QZSS assistance. */
+ @Nullable private final QzssAssistance mQzssAssistance;
+
+ private GnssAssistance(Builder builder) {
+ mGpsAssistance = builder.mGpsAssistance;
+ mGlonassAssistance = builder.mGlonassAssistance;
+ mGalileoAssistance = builder.mGalileoAssistance;
+ mBeidouAssistance = builder.mBeidouAssistance;
+ mQzssAssistance = builder.mQzssAssistance;
+ }
+
+ /** Returns the GPS assistance. */
+ @Nullable
+ public GpsAssistance getGpsAssistance() {
+ return mGpsAssistance;
+ }
+
+ /** Returns the Glonass assistance. */
+ @Nullable
+ public GlonassAssistance getGlonassAssistance() {
+ return mGlonassAssistance;
+ }
+
+ /** Returns the Galileo assistance. */
+ @Nullable
+ public GalileoAssistance getGalileoAssistance() {
+ return mGalileoAssistance;
+ }
+
+ /** Returns the Beidou assistance. */
+ @Nullable
+ public BeidouAssistance getBeidouAssistance() {
+ return mBeidouAssistance;
+ }
+
+ /** Returns the Qzss assistance. */
+ @Nullable
+ public QzssAssistance getQzssAssistance() {
+ return mQzssAssistance;
+ }
+
+ public static final @NonNull Creator<GnssAssistance> CREATOR =
+ new Creator<GnssAssistance>() {
+ @Override
+ @NonNull
+ public GnssAssistance createFromParcel(Parcel in) {
+ return new GnssAssistance.Builder()
+ .setGpsAssistance(in.readTypedObject(GpsAssistance.CREATOR))
+ .setGlonassAssistance(in.readTypedObject(GlonassAssistance.CREATOR))
+ .setGalileoAssistance(in.readTypedObject(GalileoAssistance.CREATOR))
+ .setBeidouAssistance(in.readTypedObject(BeidouAssistance.CREATOR))
+ .setQzssAssistance(in.readTypedObject(QzssAssistance.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GnssAssistance[] newArray(int size) {
+ return new GnssAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeTypedObject(mGpsAssistance, flags);
+ parcel.writeTypedObject(mGlonassAssistance, flags);
+ parcel.writeTypedObject(mGalileoAssistance, flags);
+ parcel.writeTypedObject(mBeidouAssistance, flags);
+ parcel.writeTypedObject(mQzssAssistance, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAssistance[");
+ builder.append("gpsAssistance = ").append(mGpsAssistance);
+ builder.append(", glonassAssistance = ").append(mGlonassAssistance);
+ builder.append(", galileoAssistance = ").append(mGalileoAssistance);
+ builder.append(", beidouAssistance = ").append(mBeidouAssistance);
+ builder.append(", qzssAssistance = ").append(mQzssAssistance);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssAssistance}. */
+ public static final class Builder {
+ private GpsAssistance mGpsAssistance;
+ private GlonassAssistance mGlonassAssistance;
+ private GalileoAssistance mGalileoAssistance;
+ private BeidouAssistance mBeidouAssistance;
+ private QzssAssistance mQzssAssistance;
+
+ /** Sets the GPS assistance. */
+ @NonNull
+ public Builder setGpsAssistance(@Nullable GpsAssistance gpsAssistance) {
+ mGpsAssistance = gpsAssistance;
+ return this;
+ }
+
+ /** Sets the Glonass assistance. */
+ @NonNull
+ public Builder setGlonassAssistance(@Nullable GlonassAssistance glonassAssistance) {
+ mGlonassAssistance = glonassAssistance;
+ return this;
+ }
+
+ /** Sets the Galileo assistance. */
+ @NonNull
+ public Builder setGalileoAssistance(@Nullable GalileoAssistance galileoAssistance) {
+ mGalileoAssistance = galileoAssistance;
+ return this;
+ }
+
+ /** Sets the Beidou assistance. */
+ @NonNull
+ public Builder setBeidouAssistance(@Nullable BeidouAssistance beidouAssistance) {
+ mBeidouAssistance = beidouAssistance;
+ return this;
+ }
+
+ /** Sets the QZSS assistance. */
+ @NonNull
+ public Builder setQzssAssistance(@Nullable QzssAssistance qzssAssistance) {
+ mQzssAssistance = qzssAssistance;
+ return this;
+ }
+
+ /** Builds a {@link GnssAssistance} instance as specified by this builder. */
+ @NonNull
+ public GnssAssistance build() {
+ return new GnssAssistance(this);
+ }
+ }
+
+ /** A class contains GNSS corrections for satellites. */
+ public static final class GnssSatelliteCorrections implements Parcelable {
+ /**
+ * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or OSN
+ * number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the
+ * range of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ int mSvid;
+
+ /** List of Ionospheric corrections */
+ @NonNull List<IonosphericCorrection> mIonosphericCorrections;
+
+ /**
+ * Creates a new {@link GnssSatelliteCorrections} instance.
+ *
+ * @param svid The Pseudo-random or satellite ID number for the satellite, a.k.a. Space
+ * Vehicle (SV), or OSN number for Glonass.
+ * <p>The distinction is made by looking at the constellation field. Values must be in
+ * the range of:
+ * <p>- GPS: 1-32
+ * <p>- GLONASS: 1-25
+ * <p>- QZSS: 183-206
+ * <p>- Galileo: 1-36
+ * <p>- Beidou: 1-63
+ * @param ionosphericCorrections The list of Ionospheric corrections.
+ */
+ public GnssSatelliteCorrections(
+ @IntRange(from = 1, to = 206) int svid,
+ @NonNull final List<IonosphericCorrection> ionosphericCorrections) {
+ // Allow SV ID beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(svid >= 1);
+ Preconditions.checkNotNull(
+ ionosphericCorrections, "IonosphericCorrections cannot be null");
+ mSvid = svid;
+ mIonosphericCorrections =
+ Collections.unmodifiableList(new ArrayList<>(ionosphericCorrections));
+ }
+
+ /**
+ * Returns the Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle
+ * (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the
+ * range of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /** Returns the list of Ionospheric corrections. */
+ @NonNull
+ public List<IonosphericCorrection> getIonosphericCorrections() {
+ return mIonosphericCorrections;
+ }
+
+ public static final @NonNull Creator<GnssSatelliteCorrections> CREATOR =
+ new Creator<GnssSatelliteCorrections>() {
+ @Override
+ @NonNull
+ public GnssSatelliteCorrections createFromParcel(Parcel in) {
+ int svid = in.readInt();
+ List<IonosphericCorrection> ionosphericCorrections = new ArrayList<>();
+ in.readTypedList(ionosphericCorrections, IonosphericCorrection.CREATOR);
+ return new GnssSatelliteCorrections(svid, ionosphericCorrections);
+ }
+
+ @Override
+ public GnssSatelliteCorrections[] newArray(int size) {
+ return new GnssSatelliteCorrections[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvid);
+ parcel.writeTypedList(mIonosphericCorrections, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssSatelliteCorrections[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", ionosphericCorrections = ").append(mIonosphericCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+}
diff --git a/location/java/android/location/GnssCorrectionComponent.java b/location/java/android/location/GnssCorrectionComponent.java
new file mode 100644
index 000000000000..f55dde1c9228
--- /dev/null
+++ b/location/java/android/location/GnssCorrectionComponent.java
@@ -0,0 +1,300 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class that contains Gnss correction associated with a component (e.g. the Ionospheric error).
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssCorrectionComponent implements Parcelable {
+ /**
+ * Uniquely identifies the source of correction (e.g. "Klobuchar" for ionospheric corrections).
+ * Clients should not depend on the value of the source key but, rather, can compare
+ * before/after to detect changes.
+ */
+ @NonNull private final String mSourceKey;
+
+ /** The correction is only applicable during this time interval. */
+ @NonNull private final GnssInterval mValidityInterval;
+
+ /** Pseudorange correction. */
+ @NonNull private final PseudorangeCorrection mPseudorangeCorrection;
+
+ /**
+ * Creates a GnssCorrectionComponent.
+ *
+ * @param sourceKey Uniquely identifies the source of correction (e.g. "Klobuchar" for
+ * ionospheric corrections). Clients should not depend on the value of the source key but,
+ * rather, can compare before/after to detect changes.
+ * @param validityInterval The correction is only applicable during this time interval.
+ * @param pseudorangeCorrection Pseudorange correction.
+ */
+ public GnssCorrectionComponent(
+ @NonNull String sourceKey,
+ @NonNull GnssInterval validityInterval,
+ @NonNull PseudorangeCorrection pseudorangeCorrection) {
+ Preconditions.checkNotNull(sourceKey, "SourceKey cannot be null");
+ Preconditions.checkNotNull(validityInterval, "ValidityInterval cannot be null");
+ Preconditions.checkNotNull(pseudorangeCorrection, "PseudorangeCorrection cannot be null");
+ mSourceKey = sourceKey;
+ mValidityInterval = validityInterval;
+ mPseudorangeCorrection = pseudorangeCorrection;
+ }
+
+ /** Returns the source key of the correction. */
+ @NonNull
+ public String getSourceKey() {
+ return mSourceKey;
+ }
+
+ /** Returns the validity interval of the correction. */
+ @NonNull
+ public GnssInterval getValidityInterval() {
+ return mValidityInterval;
+ }
+
+ /** Returns the pseudorange correction. */
+ @NonNull
+ public PseudorangeCorrection getPseudorangeCorrection() {
+ return mPseudorangeCorrection;
+ }
+
+ public static final @NonNull Creator<GnssCorrectionComponent> CREATOR =
+ new Creator<GnssCorrectionComponent>() {
+ @Override
+ @NonNull
+ public GnssCorrectionComponent createFromParcel(Parcel in) {
+ String sourceKey = in.readString8();
+ GnssInterval validityInterval = in.readTypedObject(GnssInterval.CREATOR);
+ PseudorangeCorrection pseudorangeCorrection =
+ in.readTypedObject(PseudorangeCorrection.CREATOR);
+ return new GnssCorrectionComponent(
+ sourceKey, validityInterval, pseudorangeCorrection);
+ }
+
+ @Override
+ public GnssCorrectionComponent[] newArray(int size) {
+ return new GnssCorrectionComponent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mSourceKey);
+ dest.writeTypedObject(mValidityInterval, flags);
+ dest.writeTypedObject(mPseudorangeCorrection, flags);
+ }
+
+ /**
+ * Time interval referenced against the GPS epoch. The start must be less than or equal to the
+ * end. When the start equals the end, the interval is empty.
+ */
+ public static final class GnssInterval implements Parcelable {
+ /**
+ * Inclusive start of the interval in milliseconds since the GPS epoch. A timestamp matching
+ * this interval will have to be the same or after the start. Required as a reference time
+ * for the initial correction value and its rate of change over time.
+ */
+ private final long mStartMillisSinceGpsEpoch;
+
+ /**
+ * Exclusive end of the interval in milliseconds since the GPS epoch. If specified, a
+ * timestamp matching this interval will have to be before the end.
+ */
+ private final long mEndMillisSinceGpsEpoch;
+
+ /**
+ * Creates a GnssInterval.
+ *
+ * @param startMillisSinceGpsEpoch Inclusive start of the interval in milliseconds since the
+ * GPS epoch. A timestamp matching this interval will have to be the same or after the
+ * start. Required as a reference time for the initial correction value and its rate of
+ * change over time.
+ * @param endMillisSinceGpsEpoch Exclusive end of the interval in milliseconds since the GPS
+ * epoch. If specified, a timestamp matching this interval will have to be before the
+ * end.
+ */
+ public GnssInterval(
+ @IntRange(from = 0) long startMillisSinceGpsEpoch,
+ @IntRange(from = 0) long endMillisSinceGpsEpoch) {
+ Preconditions.checkArgument(startMillisSinceGpsEpoch >= 0);
+ Preconditions.checkArgument(endMillisSinceGpsEpoch >= 0);
+ mStartMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+ mEndMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+ }
+
+ /** Returns the inclusive start of the interval in milliseconds since the GPS epoch. */
+ @IntRange(from = 0)
+ public long getStartMillisSinceGpsEpoch() {
+ return mStartMillisSinceGpsEpoch;
+ }
+
+ /** Returns the exclusive end of the interval in milliseconds since the GPS epoch. */
+ @IntRange(from = 0)
+ public long getEndMillisSinceGpsEpoch() {
+ return mEndMillisSinceGpsEpoch;
+ }
+
+ public static final @NonNull Creator<GnssInterval> CREATOR =
+ new Creator<GnssInterval>() {
+ @Override
+ @NonNull
+ public GnssInterval createFromParcel(Parcel in) {
+ return new GnssInterval(in.readLong(), in.readLong());
+ }
+
+ @Override
+ public GnssInterval[] newArray(int size) {
+ return new GnssInterval[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mStartMillisSinceGpsEpoch);
+ parcel.writeLong(mEndMillisSinceGpsEpoch);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssInterval[");
+ builder.append("startMillisSinceGpsEpoch = ").append(mStartMillisSinceGpsEpoch);
+ builder.append(", endMillisSinceGpsEpoch = ").append(mEndMillisSinceGpsEpoch);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /** Pseudorange correction. */
+ public static final class PseudorangeCorrection implements Parcelable {
+
+ /** Correction to be added to the measured pseudorange, in meters. */
+ private final double mCorrectionMeters;
+
+ /** Uncertainty of the correction, in meters. */
+ private final double mCorrectionUncertaintyMeters;
+
+ /**
+ * Linear approximation of the change in correction over time. Intended usage is to adjust
+ * the correction using the formula: correctionMeters + correctionRateMetersPerSecond *
+ * delta_seconds Where `delta_seconds` is the number of elapsed seconds since the beginning
+ * of the correction validity interval.
+ */
+ private final double mCorrectionRateMetersPerSecond;
+
+ /**
+ * Creates a PseudorangeCorrection.
+ *
+ * @param correctionMeters Correction to be added to the measured pseudorange, in meters.
+ * @param correctionUncertaintyMeters Uncertainty of the correction, in meters.
+ * @param correctionRateMetersPerSecond Linear approximation of the change in correction
+ * over time. Intended usage is to adjust the correction using the formula:
+ * correctionMeters + correctionRateMetersPerSecond * delta_seconds Where
+ * `delta_seconds` is the number of elapsed seconds since the beginning of the
+ * correction validity interval.
+ */
+ public PseudorangeCorrection(
+ double correctionMeters,
+ double correctionUncertaintyMeters,
+ double correctionRateMetersPerSecond) {
+ Preconditions.checkArgument(correctionUncertaintyMeters >= 0);
+ mCorrectionMeters = correctionMeters;
+ mCorrectionUncertaintyMeters = correctionUncertaintyMeters;
+ mCorrectionRateMetersPerSecond = correctionRateMetersPerSecond;
+ }
+
+ /** Returns the correction to be added to the measured pseudorange, in meters. */
+ public double getCorrectionMeters() {
+ return mCorrectionMeters;
+ }
+
+ /** Returns the uncertainty of the correction, in meters. */
+ @FloatRange(from = 0.0f)
+ public double getCorrectionUncertaintyMeters() {
+ return mCorrectionUncertaintyMeters;
+ }
+
+ /** Returns the linear approximation of the change in correction over time. */
+ public double getCorrectionRateMetersPerSecond() {
+ return mCorrectionRateMetersPerSecond;
+ }
+
+ public static final @NonNull Creator<PseudorangeCorrection> CREATOR =
+ new Creator<PseudorangeCorrection>() {
+ @Override
+ @NonNull
+ public PseudorangeCorrection createFromParcel(Parcel in) {
+ return new PseudorangeCorrection(
+ in.readDouble(), in.readDouble(), in.readDouble());
+ }
+
+ @Override
+ public PseudorangeCorrection[] newArray(int size) {
+ return new PseudorangeCorrection[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mCorrectionMeters);
+ parcel.writeDouble(mCorrectionUncertaintyMeters);
+ parcel.writeDouble(mCorrectionRateMetersPerSecond);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("PseudorangeCorrection[");
+ builder.append("correctionMeters = ").append(mCorrectionMeters);
+ builder.append(", correctionUncertaintyMeters = ").append(mCorrectionUncertaintyMeters);
+ builder.append(", correctionRateMetersPerSecond = ")
+ .append(mCorrectionRateMetersPerSecond);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+}
diff --git a/location/java/android/location/GpsAssistance.java b/location/java/android/location/GpsAssistance.java
new file mode 100644
index 000000000000..5202fc4cd851
--- /dev/null
+++ b/location/java/android/location/GpsAssistance.java
@@ -0,0 +1,289 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains GPS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GpsAssistance implements Parcelable {
+
+ /** The GPS almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of GPS ephemeris. */
+ @NonNull private final List<GpsSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of GPS satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GpsAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the GPS almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of GPS ephemeris. */
+ @NonNull
+ public List<GpsSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of GPS satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ public static final @NonNull Creator<GpsAssistance> CREATOR =
+ new Creator<GpsAssistance>() {
+ @Override
+ @NonNull
+ public GpsAssistance createFromParcel(Parcel in) {
+ return new GpsAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GpsSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GpsAssistance[] newArray(int size) {
+ return new GpsAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<GpsSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the GPS almanac. */
+ @NonNull
+ public Builder setAlmanac(
+ @Nullable @SuppressLint("NullableCollection") GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(
+ @Nullable @SuppressLint("NullableCollection")
+ KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(
+ @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(
+ @Nullable @SuppressLint("NullableCollection") LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of GPS ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GpsSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of GPS satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds a {@link GpsAssistance} instance as specified by this builder. */
+ @NonNull
+ public GpsAssistance build() {
+ return new GpsAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GpsSatelliteEphemeris.java b/location/java/android/location/GpsSatelliteEphemeris.java
new file mode 100644
index 000000000000..ec6bc59dc69c
--- /dev/null
+++ b/location/java/android/location/GpsSatelliteEphemeris.java
@@ -0,0 +1,632 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * This class contains ephemeris parameters specific to GPS satellites.
+ *
+ * <p>This is defined in IS-GPS-200 section 20.3.3.3.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GpsSatelliteEphemeris implements Parcelable {
+ /** Satellite PRN */
+ private final int mPrn;
+
+ /** L2 parameters. */
+ @NonNull private final GpsL2Params mGpsL2Params;
+
+ /** Clock model. */
+ @NonNull private final GpsSatelliteClockModel mSatelliteClockModel;
+
+ /** Orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GpsSatelliteHealth mSatelliteHealth;
+
+ /** Ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private GpsSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mGpsL2Params, "GPSL2Params cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mGpsL2Params = builder.mGpsL2Params;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 1, to = 32)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the L2 parameters of the satellite. */
+ @NonNull
+ public GpsL2Params getGpsL2Params() {
+ return mGpsL2Params;
+ }
+
+ /** Returns the clock model of the satellite. */
+ @NonNull
+ public GpsSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the orbit model of the satellite. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GpsSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteEphemeris> CREATOR =
+ new Creator<GpsSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public GpsSatelliteEphemeris createFromParcel(Parcel in) {
+ final GpsSatelliteEphemeris.Builder gpsSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
+ .setSatelliteClockModel(
+ in.readTypedObject(GpsSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(GpsSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return gpsSatelliteEphemeris.build();
+ }
+
+ @Override
+ public GpsSatelliteEphemeris[] newArray(int size) {
+ return new GpsSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mGpsL2Params, flags);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteEphemeris[");
+ builder.append("prn = ").append(mPrn);
+ builder.append(", gpsL2Params = ").append(mGpsL2Params);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteEphemeris} */
+ public static final class Builder {
+ private int mPrn = 0;
+ private GpsL2Params mGpsL2Params;
+ private GpsSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GpsSatelliteHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(@IntRange(from = 1, to = 32) int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the L2 parameters of the satellite. */
+ @NonNull
+ public Builder setGpsL2Params(@NonNull GpsL2Params gpsL2Params) {
+ mGpsL2Params = gpsL2Params;
+ return this;
+ }
+
+ /** Sets the clock model of the satellite. */
+ @NonNull
+ public Builder setSatelliteClockModel(@NonNull GpsSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the orbit model of the satellite. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GpsSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteEphemeris build() {
+ return new GpsSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains information about GPS health. The information is tied to Legacy Navigation
+ * (LNAV) data, not Civil Navigation (CNAV) data.
+ */
+ public static final class GpsSatelliteHealth implements Parcelable {
+ /**
+ * Represents "SV health" in the "BROADCAST ORBIT - 6" record of RINEX 3.05. Table A6,
+ * pp.68.
+ */
+ private final int mSvHealth;
+
+ /**
+ * Represents "SV accuracy" in meters in the "BROADCAST ORBIT - 6" record of RINEX 3.05.
+ * Table A6, pp.68.
+ */
+ private final double mSvAccur;
+
+ /**
+ * Represents the "Fit Interval" in hours in the "BROADCAST ORBIT - 7" record of RINEX 3.05.
+ * Table A6, pp.69.
+ */
+ private final double mFitInt;
+
+ /** Returns the SV health. */
+ @IntRange(from = 0, to = 63)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the SV accuracy in meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getSvAccur() {
+ return mSvAccur;
+ }
+
+ /** Returns the fit interval in hours. */
+ @FloatRange(from = 0.0f)
+ public double getFitInt() {
+ return mFitInt;
+ }
+
+ private GpsSatelliteHealth(Builder builder) {
+ // Allow SV health beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgumentInRange(builder.mSvAccur, 0.0f, 8192.0f, "SvAccur");
+ Preconditions.checkArgument(builder.mFitInt >= 0.0f);
+ mSvHealth = builder.mSvHealth;
+ mSvAccur = builder.mSvAccur;
+ mFitInt = builder.mFitInt;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteHealth> CREATOR =
+ new Creator<GpsSatelliteHealth>() {
+ @Override
+ @NonNull
+ public GpsSatelliteHealth createFromParcel(Parcel in) {
+ final GpsSatelliteHealth.Builder gpsSatelliteHealth =
+ new Builder()
+ .setSvHealth(in.readInt())
+ .setSvAccur(in.readDouble())
+ .setFitInt(in.readDouble());
+ return gpsSatelliteHealth.build();
+ }
+
+ @Override
+ public GpsSatelliteHealth[] newArray(int size) {
+ return new GpsSatelliteHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvHealth);
+ parcel.writeDouble(mSvAccur);
+ parcel.writeDouble(mFitInt);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteHealth[");
+ builder.append("svHealth = ").append(mSvHealth);
+ builder.append(", svAccur = ").append(mSvAccur);
+ builder.append(", fitInt = ").append(mFitInt);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteHealth}. */
+ public static final class Builder {
+ private int mSvHealth;
+ private double mSvAccur;
+ private double mFitInt;
+
+ /** Sets the SV health. */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0, to = 63) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the SV accuracy in meters. */
+ @NonNull
+ public Builder setSvAccur(@FloatRange(from = 0.0f, to = 8192.0f) double svAccur) {
+ mSvAccur = svAccur;
+ return this;
+ }
+
+ /** Sets the fit interval in hours. */
+ @NonNull
+ public Builder setFitInt(@FloatRange(from = 0.0f) double fitInt) {
+ mFitInt = fitInt;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteHealth} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteHealth build() {
+ return new GpsSatelliteHealth(this);
+ }
+ }
+ }
+
+ /** A class contains L2 parameters specific to GPS satellites. */
+ public static final class GpsL2Params implements Parcelable {
+ /** Code(s) on L2 Channel. */
+ private final int mL2Code;
+
+ /** Data Flag for L2 P-Code. */
+ private final int mL2Flag;
+
+ /** Returns the code(s) on L2 channel. */
+ @IntRange(from = 0, to = 3)
+ public int getL2Code() {
+ return mL2Code;
+ }
+
+ /** Returns the data flag for L2 P-code. */
+ @IntRange(from = 0, to = 1)
+ public int getL2Flag() {
+ return mL2Flag;
+ }
+
+ private GpsL2Params(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mL2Code, 0, 3, "L2 code");
+ Preconditions.checkArgumentInRange(builder.mL2Flag, 0, 1, "L2 flag");
+ mL2Code = builder.mL2Code;
+ mL2Flag = builder.mL2Flag;
+ }
+
+ public static final @NonNull Creator<GpsL2Params> CREATOR =
+ new Creator<GpsL2Params>() {
+ @Override
+ @NonNull
+ public GpsL2Params createFromParcel(Parcel in) {
+ final GpsL2Params.Builder gpsL2Params =
+ new Builder().setL2Code(in.readInt()).setL2Flag(in.readInt());
+ return gpsL2Params.build();
+ }
+
+ @Override
+ public GpsL2Params[] newArray(int size) {
+ return new GpsL2Params[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mL2Code);
+ parcel.writeInt(mL2Flag);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsL2Params[");
+ builder.append("l2Code = ").append(mL2Code);
+ builder.append(", l2Flag = ").append(mL2Flag);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsL2Params}. */
+ public static final class Builder {
+ private int mL2Code = 0;
+ private int mL2Flag = 0;
+
+ /** Sets the code(s) on L2 channel. */
+ @NonNull
+ public Builder setL2Code(@IntRange(from = 0, to = 3) int l2Code) {
+ mL2Code = l2Code;
+ return this;
+ }
+
+ /** Sets the data flag for L2 P-code. */
+ @NonNull
+ public Builder setL2Flag(@IntRange(from = 0, to = 1) int l2Flag) {
+ mL2Flag = l2Flag;
+ return this;
+ }
+
+ /** Builds a {@link GpsL2Params} instance as specified by this builder. */
+ @NonNull
+ public GpsL2Params build() {
+ return new GpsL2Params(this);
+ }
+ }
+ }
+
+ /** A class contains the set of parameters needed for GPS satellite clock correction. */
+ public static final class GpsSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds since GPS epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of GPS
+ * navigation message in RINEX 3.05 Table A6.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias in seconds. */
+ private final double mAf0;
+
+ /** SV clock drift in seconds per second. */
+ private final double mAf1;
+
+ /** Clock drift rate in seconds per second squared. */
+ private final double mAf2;
+
+ /** Group delay differential in seconds. */
+ private final double mTgd;
+
+ /** Issue of data, clock. */
+ private final int mIodc;
+
+ private GpsSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -9.77e-3f, 9.77e-3f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -3.73e-9f, 3.73e-9f, "Af1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -3.56e-15f, 3.56e-15f, "Af2");
+ Preconditions.checkArgumentInRange(builder.mTgd, -5.97e-8f, 5.97e-8f, "Tgd");
+ Preconditions.checkArgumentInRange(builder.mIodc, 0, 1023, "Iodc");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mTgd = builder.mTgd;
+ mIodc = builder.mIodc;
+ }
+
+ /** Returns the time of the clock in seconds since GPS epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias in seconds. */
+ @FloatRange(from = -9.77e-3f, to = 9.77e-3f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift in seconds per second. */
+ @FloatRange(from = -3.73e-9f, to = 3.73e-9f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the clock drift rate in seconds per second squared. */
+ @FloatRange(from = -3.56e-15f, to = 3.56e-15f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the group delay differential in seconds. */
+ @FloatRange(from = -5.97e-8f, to = 5.97e-8f)
+ public double getTgd() {
+ return mTgd;
+ }
+
+ /** Returns the issue of data, clock. */
+ @IntRange(from = 0, to = 1023)
+ public int getIodc() {
+ return mIodc;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteClockModel> CREATOR =
+ new Creator<GpsSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public GpsSatelliteClockModel createFromParcel(Parcel in) {
+ final GpsSatelliteClockModel.Builder gpsSatelliteClockModel =
+ new Builder()
+ .setTimeOfClockSeconds(in.readLong())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble())
+ .setAf2(in.readDouble())
+ .setTgd(in.readDouble())
+ .setIodc(in.readInt());
+ return gpsSatelliteClockModel.build();
+ }
+
+ @Override
+ public GpsSatelliteClockModel[] newArray(int size) {
+ return new GpsSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mTgd);
+ parcel.writeInt(mIodc);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", tgd = ").append(mTgd);
+ builder.append(", iodc = ").append(mIodc);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mTgd;
+ private int mIodc;
+
+ /** Sets the time of the clock in seconds since GPS epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -9.77e-3f, to = 9.77e-3f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -3.73e-9f, to = 3.73e-9f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Sets the clock drift rate in seconds per second squared. */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -3.56e-15f, to = 3.56e-15f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the group delay differential in seconds. */
+ @NonNull
+ public Builder setTgd(@FloatRange(from = -5.97e-8f, to = 5.97e-8f) double tgd) {
+ mTgd = tgd;
+ return this;
+ }
+
+ /** Sets the issue of data, clock. */
+ @NonNull
+ public Builder setIodc(@IntRange(from = 0, to = 1023) int iodc) {
+ mIodc = iodc;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteClockModel} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteClockModel build() {
+ return new GpsSatelliteClockModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/IonosphericCorrection.java b/location/java/android/location/IonosphericCorrection.java
new file mode 100644
index 000000000000..aafcf8301add
--- /dev/null
+++ b/location/java/android/location/IonosphericCorrection.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ionospheric correction.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class IonosphericCorrection implements Parcelable {
+
+ /** Carrier frequency in Hz to differentiate signals from the same satellite. e.g. GPS L1/L5 */
+ private final long mCarrierFrequencyHz;
+
+ /** Ionospheric correction. */
+ @NonNull private final GnssCorrectionComponent mIonosphericCorrection;
+
+ /**
+ * Creates a new {@link IonosphericCorrection} instance.
+ *
+ * @param carrierFrequencyHz Carrier frequency in Hz to differentiate signals from the
+ * samesatellite. e.g. GPS L1/L5
+ * @param ionosphericCorrection Ionospheric correction.
+ */
+ public IonosphericCorrection(
+ @IntRange(from = 0) long carrierFrequencyHz,
+ @NonNull GnssCorrectionComponent ionosphericCorrection) {
+ Preconditions.checkArgument(carrierFrequencyHz > 0);
+ Preconditions.checkNotNull(ionosphericCorrection, "IonosphericCorrection cannot be null");
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ mIonosphericCorrection = ionosphericCorrection;
+ }
+
+ /**
+ * Returns the carrier frequency in Hz to differentiate signals from the same satellite. e.g.
+ * GPS L1/L5
+ */
+ @IntRange(from = 0)
+ public long getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ /** Returns the Ionospheric correction. */
+ @NonNull
+ public GnssCorrectionComponent getIonosphericCorrection() {
+ return mIonosphericCorrection;
+ }
+
+ public static final @NonNull Creator<IonosphericCorrection> CREATOR =
+ new Creator<IonosphericCorrection>() {
+ @Override
+ @NonNull
+ public IonosphericCorrection createFromParcel(Parcel in) {
+ long carrierFrequencyHz = in.readLong();
+ GnssCorrectionComponent ionosphericCorrection =
+ in.readTypedObject(GnssCorrectionComponent.CREATOR);
+ return new IonosphericCorrection(carrierFrequencyHz, ionosphericCorrection);
+ }
+
+ @Override
+ public IonosphericCorrection[] newArray(int size) {
+ return new IonosphericCorrection[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mCarrierFrequencyHz);
+ dest.writeTypedObject(mIonosphericCorrection, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("IonosphericCorrection[");
+ builder.append("carrierFrequencyHz = ").append(mCarrierFrequencyHz);
+ builder.append(", ionosphericCorrection = ").append(mIonosphericCorrection);
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git a/location/java/android/location/KeplerianOrbitModel.java b/location/java/android/location/KeplerianOrbitModel.java
new file mode 100644
index 000000000000..a118274dc9a3
--- /dev/null
+++ b/location/java/android/location/KeplerianOrbitModel.java
@@ -0,0 +1,518 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains Keplerian orbit model parameters for GPS/Galileo/QZSS/Beidou.
+ * <p>For GPS, this is defined in IS-GPS-200 Table 20-II.
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.1.
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.
+ * <p>For Baidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.12.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class KeplerianOrbitModel implements Parcelable {
+ /** Square root of the semi-major axis in square root of meters. */
+ private final double mRootA;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /** Inclination angle at reference time in radians. */
+ private final double mI0;
+
+ /** Rate of change of inclination angle in radians per second. */
+ private final double mIDot;
+
+ /** Argument of perigee in radians. */
+ private final double mOmega;
+
+ /** Longitude of ascending node of orbit plane at beginning of week in radians. */
+ private final double mOmega0;
+
+ /** Rate of right ascension in radians per second. */
+ private final double mOmegaDot;
+
+ /** Mean anomaly at reference time in radians. */
+ private final double mM0;
+
+ /** Mean motion difference from computed value in radians per second. */
+ private final double mDeltaN;
+
+ /** Second-order harmonic perturbations. */
+ SecondOrderHarmonicPerturbation mSecondOrderHarmonicPerturbation;
+
+ private KeplerianOrbitModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mRootA, 0.0f, 8192.0f, "RootA");
+ Preconditions.checkArgumentInRange(builder.mEccentricity, 0.0f, 0.5f, "Eccentricity");
+ Preconditions.checkArgumentInRange(builder.mI0, -3.15f, 3.15f, "I0");
+ Preconditions.checkArgumentInRange(builder.mIDot, -2.94e-9f, 2.94e-9f, "IDot");
+ Preconditions.checkArgumentInRange(builder.mOmega, -3.15f, 3.15f, "Omega");
+ Preconditions.checkArgumentInRange(builder.mOmega0, -3.15f, 3.15f, "Omega0");
+ Preconditions.checkArgumentInRange(builder.mOmegaDot, -3.1e-6f, 3.1e-6f, "OmegaDot");
+ Preconditions.checkArgumentInRange(builder.mM0, -3.15f, 3.15f, "M0");
+ Preconditions.checkArgumentInRange(builder.mDeltaN, -1.18e-8f, 1.18e-8f, "DeltaN");
+ mRootA = builder.mRootA;
+ mEccentricity = builder.mEccentricity;
+ mI0 = builder.mI0;
+ mIDot = builder.mIDot;
+ mOmega = builder.mOmega;
+ mOmega0 = builder.mOmega0;
+ mOmegaDot = builder.mOmegaDot;
+ mM0 = builder.mM0;
+ mDeltaN = builder.mDeltaN;
+ mSecondOrderHarmonicPerturbation = builder.mSecondOrderHarmonicPerturbation;
+ }
+
+ /** Get the square root of the semi-major axis in square root of meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getRootA() {
+ return mRootA;
+ }
+
+ /** Get the eccentricity. */
+ @FloatRange(from = 0.0f, to = 0.5f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /** Get the inclination angle at reference time in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getI0() {
+ return mI0;
+ }
+
+ /** Get the rate of change of inclination angle in radians per second. */
+ @FloatRange(from = -2.94e-9f, to = 2.94e-9f)
+ public double getIDot() {
+ return mIDot;
+ }
+
+ /** Get the argument of perigee in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ /** Get the longitude of ascending node of orbit plane at beginning of week in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getOmega0() {
+ return mOmega0;
+ }
+
+ /** Get the rate of right ascension in radians per second. */
+ @FloatRange(from = -3.1e-6f, to = 3.1e-6f)
+ public double getOmegaDot() {
+ return mOmegaDot;
+ }
+
+ /** Get the mean anomaly at reference time in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getM0() {
+ return mM0;
+ }
+
+ /** Get the mean motion difference from computed value in radians per second. */
+ @FloatRange(from = -1.18e-8f, to = 1.18e-8f)
+ public double getDeltaN() {
+ return mDeltaN;
+ }
+
+ /** Get the second-order harmonic perturbations. */
+ @NonNull
+ public SecondOrderHarmonicPerturbation getSecondOrderHarmonicPerturbation() {
+ return mSecondOrderHarmonicPerturbation;
+ }
+
+ public static final @NonNull Creator<KeplerianOrbitModel> CREATOR =
+ new Creator<KeplerianOrbitModel>() {
+ @Override
+ @NonNull
+ public KeplerianOrbitModel createFromParcel(Parcel in) {
+ final KeplerianOrbitModel.Builder keplerianOrbitModel =
+ new Builder()
+ .setRootA(in.readDouble())
+ .setEccentricity(in.readDouble())
+ .setI0(in.readDouble())
+ .setIDot(in.readDouble())
+ .setOmega(in.readDouble())
+ .setOmega0(in.readDouble())
+ .setOmegaDot(in.readDouble())
+ .setM0(in.readDouble())
+ .setDeltaN(in.readDouble())
+ .setSecondOrderHarmonicPerturbation(
+ in.readTypedObject(
+ SecondOrderHarmonicPerturbation.CREATOR));
+ return keplerianOrbitModel.build();
+ }
+
+ @Override
+ public KeplerianOrbitModel[] newArray(int size) {
+ return new KeplerianOrbitModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mRootA);
+ parcel.writeDouble(mEccentricity);
+ parcel.writeDouble(mI0);
+ parcel.writeDouble(mIDot);
+ parcel.writeDouble(mOmega);
+ parcel.writeDouble(mOmega0);
+ parcel.writeDouble(mOmegaDot);
+ parcel.writeDouble(mM0);
+ parcel.writeDouble(mDeltaN);
+ parcel.writeTypedObject(mSecondOrderHarmonicPerturbation, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("KeplerianOrbitModel[");
+ builder.append("rootA = ").append(mRootA);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", i0 = ").append(mI0);
+ builder.append(", iDot = ").append(mIDot);
+ builder.append(", omega = ").append(mOmega);
+ builder.append(", omega0 = ").append(mOmega0);
+ builder.append(", omegaDot = ").append(mOmegaDot);
+ builder.append(", m0 = ").append(mM0);
+ builder.append(", deltaN = ").append(mDeltaN);
+ builder.append(", secondOrderHarmonicPerturbation = ")
+ .append(mSecondOrderHarmonicPerturbation);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link KeplerianOrbitModel} */
+ public static final class Builder {
+ private double mRootA;
+ private double mEccentricity;
+ private double mI0;
+ private double mIDot;
+ private double mOmega;
+ private double mOmega0;
+ private double mOmegaDot;
+ private double mM0;
+ private double mDeltaN;
+ private SecondOrderHarmonicPerturbation mSecondOrderHarmonicPerturbation;
+
+ /** Sets the square root of the semi-major axis in square root of meters. */
+ @NonNull
+ public Builder setRootA(@FloatRange(from = 0.0f, to = 8192.0f) double rootA) {
+ mRootA = rootA;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(@FloatRange(from = 0.0f, to = 0.5f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /** Sets the inclination angle at reference time in radians. */
+ @NonNull
+ public Builder setI0(@FloatRange(from = -3.15f, to = 3.15f) double i0) {
+ mI0 = i0;
+ return this;
+ }
+
+ /** Sets the rate of change of inclination angle in radians per second. */
+ @NonNull
+ public Builder setIDot(@FloatRange(from = -2.94e-9f, to = 2.94e-9f) double iDot) {
+ mIDot = iDot;
+ return this;
+ }
+
+ /** Sets the argument of perigee in radians. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -3.15f, to = 3.15f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of ascending node of orbit plane at beginning of week in radians.
+ */
+ @NonNull
+ public Builder setOmega0(@FloatRange(from = -3.15f, to = 3.15f) double omega0) {
+ mOmega0 = omega0;
+ return this;
+ }
+
+ /** Sets the rate of right ascension in radians per second. */
+ @NonNull
+ public Builder setOmegaDot(@FloatRange(from = -3.1e-6f, to = 3.1e-6f) double omegaDot) {
+ mOmegaDot = omegaDot;
+ return this;
+ }
+
+ /** Sets the mean anomaly at reference time in radians. */
+ @NonNull
+ public Builder setM0(@FloatRange(from = -3.15f, to = 3.15f) double m0) {
+ mM0 = m0;
+ return this;
+ }
+
+ /** Sets the mean motion difference from computed value in radians per second. */
+ @NonNull
+ public Builder setDeltaN(@FloatRange(from = -1.18e-8f, to = 1.18e-8f) double deltaN) {
+ mDeltaN = deltaN;
+ return this;
+ }
+
+ /** Sets the second-order harmonic perturbations. */
+ @NonNull
+ public Builder setSecondOrderHarmonicPerturbation(
+ @NonNull SecondOrderHarmonicPerturbation secondOrderHarmonicPerturbation) {
+ mSecondOrderHarmonicPerturbation = secondOrderHarmonicPerturbation;
+ return this;
+ }
+
+ /** Builds a {@link KeplerianOrbitModel} instance as specified by this builder. */
+ @NonNull
+ public KeplerianOrbitModel build() {
+ return new KeplerianOrbitModel(this);
+ }
+ }
+
+ /** A class contains second-order harmonic perturbations. */
+ public static final class SecondOrderHarmonicPerturbation implements Parcelable {
+ /** Amplitude of cosine harmonic correction term to angle of inclination in radians. */
+ private final double mCic;
+
+ /** Amplitude of sine harmonic correction term to angle of inclination in radians. */
+ private final double mCis;
+
+ /** Amplitude of cosine harmonic correction term to the orbit in meters. */
+ private final double mCrc;
+
+ /** Amplitude of sine harmonic correction term to the orbit in meters. */
+ private final double mCrs;
+
+ /** Amplitude of cosine harmonic correction term to the argument of latitude in radians. */
+ private final double mCuc;
+
+ /** Amplitude of sine harmonic correction term to the argument of latitude in radians. */
+ private final double mCus;
+
+ private SecondOrderHarmonicPerturbation(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mCic, -6.11e-5f, 6.11e-5f, "Cic");
+ Preconditions.checkArgumentInRange(builder.mCis, -6.11e-5f, 6.11e-5f, "Cis");
+ Preconditions.checkArgumentInRange(builder.mCrc, -2048.0f, 2048.0f, "Crc");
+ Preconditions.checkArgumentInRange(builder.mCrs, -2048.0f, 2048.0f, "Crs");
+ Preconditions.checkArgumentInRange(builder.mCuc, -6.11e-5f, 6.11e-5f, "Cuc");
+ Preconditions.checkArgumentInRange(builder.mCus, -6.11e-5f, 6.11e-5f, "Cus");
+ mCic = builder.mCic;
+ mCrc = builder.mCrc;
+ mCis = builder.mCis;
+ mCrs = builder.mCrs;
+ mCuc = builder.mCuc;
+ mCus = builder.mCus;
+ }
+
+ /**
+ * Get the amplitude of cosine harmonic correction term to angle of inclination in radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCic() {
+ return mCic;
+ }
+
+ /**
+ * Get the amplitude of sine harmonic correction term to angle of inclination in radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCis() {
+ return mCis;
+ }
+
+ /** Get the amplitude of cosine harmonic correction term to the orbit in meters. */
+ @FloatRange(from = -2048.0f, to = 2048.0f)
+ public double getCrc() {
+ return mCrc;
+ }
+
+ /** Get the amplitude of sine harmonic correction term to the orbit in meters. */
+ @FloatRange(from = -2048.0f, to = 2048.0f)
+ public double getCrs() {
+ return mCrs;
+ }
+
+ /**
+ * Get the amplitude of cosine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCuc() {
+ return mCuc;
+ }
+
+ /**
+ * Get the amplitude of sine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCus() {
+ return mCus;
+ }
+
+ public static final @NonNull Creator<SecondOrderHarmonicPerturbation> CREATOR =
+ new Creator<SecondOrderHarmonicPerturbation>() {
+ @Override
+ @NonNull
+ public SecondOrderHarmonicPerturbation createFromParcel(Parcel in) {
+ final SecondOrderHarmonicPerturbation.Builder
+ secondOrderHarmonicPerturbation =
+ new Builder()
+ .setCic(in.readDouble())
+ .setCis(in.readDouble())
+ .setCrc(in.readDouble())
+ .setCrs(in.readDouble())
+ .setCuc(in.readDouble())
+ .setCus(in.readDouble());
+ return secondOrderHarmonicPerturbation.build();
+ }
+
+ @Override
+ public SecondOrderHarmonicPerturbation[] newArray(int size) {
+ return new SecondOrderHarmonicPerturbation[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mCic);
+ parcel.writeDouble(mCis);
+ parcel.writeDouble(mCrc);
+ parcel.writeDouble(mCrs);
+ parcel.writeDouble(mCuc);
+ parcel.writeDouble(mCus);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("SecondOrderHarmonicPerturbation[");
+ builder.append("cic = ").append(mCic);
+ builder.append(", cis = ").append(mCis);
+ builder.append(", crc = ").append(mCrc);
+ builder.append(", crs = ").append(mCrs);
+ builder.append(", cuc = ").append(mCuc);
+ builder.append(", cus = ").append(mCus);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link SecondOrderHarmonicPerturbation} */
+ public static final class Builder {
+ private double mCic;
+ private double mCis;
+ private double mCrc;
+ private double mCrs;
+ private double mCuc;
+ private double mCus;
+
+ /**
+ * Sets the amplitude of cosine harmonic correction term to angle of inclination in
+ * radians.
+ */
+ @NonNull
+ public Builder setCic(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cic) {
+ mCic = cic;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of sine harmonic correction term to angle of inclination in
+ * radians.
+ */
+ @NonNull
+ public Builder setCis(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cis) {
+ mCis = cis;
+ return this;
+ }
+
+ /** Sets the amplitude of cosine harmonic correction term to the orbit in meters. */
+ @NonNull
+ public Builder setCrc(@FloatRange(from = -2048.0f, to = 2048.0f) double crc) {
+ mCrc = crc;
+ return this;
+ }
+
+ /** Sets the amplitude of sine harmonic correction term to the orbit in meters. */
+ @NonNull
+ public Builder setCrs(@FloatRange(from = -2048.0f, to = 2048.0f) double crs) {
+ mCrs = crs;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of cosine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @NonNull
+ public Builder setCuc(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cuc) {
+ mCuc = cuc;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of sine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @NonNull
+ public Builder setCus(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cus) {
+ mCus = cus;
+ return this;
+ }
+
+ /**
+ * Builds a {@link SecondOrderHarmonicPerturbation} instance as specified by this
+ * builder.
+ */
+ @NonNull
+ public SecondOrderHarmonicPerturbation build() {
+ return new SecondOrderHarmonicPerturbation(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/KlobucharIonosphericModel.java b/location/java/android/location/KlobucharIonosphericModel.java
new file mode 100644
index 000000000000..d239c876f702
--- /dev/null
+++ b/location/java/android/location/KlobucharIonosphericModel.java
@@ -0,0 +1,252 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains Klobuchar ionospheric model coefficients used by GPS, BDS, QZSS.
+ *
+ * <p>This is defined in IS-GPS-200 section 20.3.3.5.1.7.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class KlobucharIonosphericModel implements Parcelable {
+ /** Alpha0 coefficientin seconds. */
+ double mAlpha0;
+ /** Alpha1 coefficient in seconds per semi-circle. */
+ double mAlpha1;
+ /** Alpha2 coefficient in seconds per semi-circle squared. */
+ double mAlpha2;
+ /** Alpha3 coefficient in seconds per semi-circle cubed. */
+ double mAlpha3;
+ /** Beta0 coefficient in seconds. */
+ double mBeta0;
+ /** Beta1 coefficient in seconds per semi-circle. */
+ double mBeta1;
+ /** Beta2 coefficient in seconds per semi-circle squared. */
+ double mBeta2;
+ /** Beta3 coefficient in seconds per semi-circle cubed. */
+ double mBeta3;
+
+ private KlobucharIonosphericModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mAlpha0, -1.193e-7f, 1.193e-7f, "Alpha0");
+ Preconditions.checkArgumentInRange(builder.mAlpha1, -9.54e-7f, 9.54e-7f, "Alpha1");
+ Preconditions.checkArgumentInRange(builder.mAlpha2, -7.63e-6f, 7.63e-6f, "Alpha2");
+ Preconditions.checkArgumentInRange(builder.mAlpha3, -7.63e-6f, 7.63e-6f, "Alpha3");
+ Preconditions.checkArgumentInRange(builder.mBeta0, -262144.0f, 262144.0f, "Beta0");
+ Preconditions.checkArgumentInRange(builder.mBeta1, -2097152.0f, 2097152.0f, "Beta1");
+ Preconditions.checkArgumentInRange(builder.mBeta2, -8388608.0f, 8388608.0f, "Beta2");
+ Preconditions.checkArgumentInRange(builder.mBeta3, -8388608.0f, 8388608.0f, "Beta3");
+ mAlpha0 = builder.mAlpha0;
+ mAlpha1 = builder.mAlpha1;
+ mAlpha2 = builder.mAlpha2;
+ mAlpha3 = builder.mAlpha3;
+ mBeta0 = builder.mBeta0;
+ mBeta1 = builder.mBeta1;
+ mBeta2 = builder.mBeta2;
+ mBeta3 = builder.mBeta3;
+ }
+
+ /** Returns the alpha0 coefficient in seconds. */
+ @FloatRange(from = -1.193e-7f, to = 1.193e-7f)
+ public double getAlpha0() {
+ return mAlpha0;
+ }
+
+ /** Returns the alpha1 coefficient in seconds per semi-circle. */
+ @FloatRange(from = -9.54e-7f, to = 9.54e-7f)
+ public double getAlpha1() {
+ return mAlpha1;
+ }
+
+ /** Returns the alpha2 coefficient in seconds per semi-circle squared. */
+ @FloatRange(from = -7.63e-6f, to = 7.63e-6f)
+ public double getAlpha2() {
+ return mAlpha2;
+ }
+
+ /** Returns the alpha3 coefficient in seconds per semi-circle cubed. */
+ @FloatRange(from = -7.63e-6f, to = 7.63e-6f)
+ public double getAlpha3() {
+ return mAlpha3;
+ }
+
+ /** Returns the beta0 coefficient in seconds. */
+ @FloatRange(from = -262144.0f, to = 262144.0f)
+ public double getBeta0() {
+ return mBeta0;
+ }
+
+ /** Returns the beta1 coefficient in seconds per semi-circle. */
+ @FloatRange(from = -2097152.0f, to = 2097152.0f)
+ public double getBeta1() {
+ return mBeta1;
+ }
+
+ /** Returns the beta2 coefficient in seconds per semi-circle squared. */
+ @FloatRange(from = -8388608.0f, to = 8388608.0f)
+ public double getBeta2() {
+ return mBeta2;
+ }
+
+ /** Returns the beta3 coefficient in seconds per semi-circle cubed. */
+ @FloatRange(from = -8388608.0f, to = 8388608.0f)
+ public double getBeta3() {
+ return mBeta3;
+ }
+
+ public static final @NonNull Creator<KlobucharIonosphericModel> CREATOR =
+ new Creator<KlobucharIonosphericModel>() {
+ @Override
+ @NonNull
+ public KlobucharIonosphericModel createFromParcel(Parcel in) {
+ return new KlobucharIonosphericModel.Builder()
+ .setAlpha0(in.readDouble())
+ .setAlpha1(in.readDouble())
+ .setAlpha2(in.readDouble())
+ .setAlpha3(in.readDouble())
+ .setBeta0(in.readDouble())
+ .setBeta1(in.readDouble())
+ .setBeta2(in.readDouble())
+ .setBeta3(in.readDouble())
+ .build();
+ }
+ @Override
+ public KlobucharIonosphericModel[] newArray(int size) {
+ return new KlobucharIonosphericModel[size];
+ }
+ };
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mAlpha0);
+ parcel.writeDouble(mAlpha1);
+ parcel.writeDouble(mAlpha2);
+ parcel.writeDouble(mAlpha3);
+ parcel.writeDouble(mBeta0);
+ parcel.writeDouble(mBeta1);
+ parcel.writeDouble(mBeta2);
+ parcel.writeDouble(mBeta3);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("KlobucharIonosphericModel[");
+ builder.append("alpha0 = ").append(mAlpha0);
+ builder.append(", alpha1 = ").append(mAlpha1);
+ builder.append(", alpha2 = ").append(mAlpha2);
+ builder.append(", alpha3 = ").append(mAlpha3);
+ builder.append(", beta0 = ").append(mBeta0);
+ builder.append(", beta1 = ").append(mBeta1);
+ builder.append(", beta2 = ").append(mBeta2);
+ builder.append(", beta3 = ").append(mBeta3);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link KlobucharIonosphericModel} */
+ public static final class Builder {
+ private double mAlpha0;
+ private double mAlpha1;
+ private double mAlpha2;
+ private double mAlpha3;
+ private double mBeta0;
+ private double mBeta1;
+ private double mBeta2;
+ private double mBeta3;
+
+ /** Sets the alpha0 coefficient in seconds. */
+ @NonNull
+ public Builder setAlpha0(@FloatRange(from = -1.193e-7f, to = 1.193e-7f) double alpha0) {
+ mAlpha0 = alpha0;
+ return this;
+ }
+
+ /** Sets the alpha1 coefficient in seconds per semi-circle. */
+ @NonNull
+ public Builder setAlpha1(@FloatRange(from = -9.54e-7f, to = 9.54e-7f) double alpha1) {
+ mAlpha1 = alpha1;
+ return this;
+ }
+
+ /** Sets the alpha2 coefficient in seconds per semi-circle squared. */
+ @NonNull
+ public Builder setAlpha2(@FloatRange(from = -7.63e-6f, to = 7.63e-6f) double alpha2) {
+ mAlpha2 = alpha2;
+ return this;
+ }
+
+ /** Sets the alpha3 coefficient in seconds per semi-circle cubed. */
+ @NonNull
+ public Builder setAlpha3(@FloatRange(from = -7.63e-6f, to = 7.63e-6f) double alpha3) {
+ mAlpha3 = alpha3;
+ return this;
+ }
+
+ /** Sets the beta0 coefficient in seconds. */
+ @NonNull
+ public Builder setBeta0(@FloatRange(from = -262144.0f, to = 262144.0f) double beta0) {
+ mBeta0 = beta0;
+ return this;
+ }
+
+ /** Sets the beta1 coefficient in seconds per semi-circle. */
+ @NonNull
+ public Builder setBeta1(@FloatRange(from = -2097152.0f, to = 2097152.0f) double beta1) {
+ mBeta1 = beta1;
+ return this;
+ }
+
+ /** Sets the beta2 coefficient in seconds per semi-circle squared. */
+ @NonNull
+ public Builder setBeta2(@FloatRange(from = -8388608.0f, to = 8388608.0f) double beta2) {
+ mBeta2 = beta2;
+ return this;
+ }
+
+ /** Sets the beta3 coefficient in seconds per semi-circle cubed. */
+ @NonNull
+ public Builder setBeta3(@FloatRange(from = -8388608.0f, to = 8388608.0f) double beta3) {
+ mBeta3 = beta3;
+ return this;
+ }
+
+ /** Builds a {@link KlobucharIonosphericModel} instance as specified by this builder. */
+ @NonNull
+ public KlobucharIonosphericModel build() {
+ return new KlobucharIonosphericModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/LeapSecondsModel.java b/location/java/android/location/LeapSecondsModel.java
new file mode 100644
index 000000000000..bc132addb06a
--- /dev/null
+++ b/location/java/android/location/LeapSecondsModel.java
@@ -0,0 +1,171 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains the leap seconds set of parameters needed for GNSS time.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class LeapSecondsModel implements Parcelable {
+ /** Time difference due to leap seconds before the event in seconds. (UTC) */
+ private final int mLeapSeconds;
+
+ /** Time difference due to leap seconds after the event in seconds. (UTC) */
+ private final int mLeapSecondsFuture;
+
+ /** GNSS week number in which the leap second event will occur. (UTC) */
+ private final int mWeekNumberLeapSecondsFuture;
+
+ /** Day number when the next leap second will occur. */
+ private final int mDayNumberLeapSecondsFuture;
+
+ private LeapSecondsModel(Builder builder) {
+ Preconditions.checkArgument(builder.mLeapSeconds >= 0);
+ Preconditions.checkArgument(builder.mLeapSecondsFuture >= 0);
+ Preconditions.checkArgument(builder.mWeekNumberLeapSecondsFuture >= 0);
+ Preconditions.checkArgument(builder.mDayNumberLeapSecondsFuture >= 0);
+ mLeapSeconds = builder.mLeapSeconds;
+ mLeapSecondsFuture = builder.mLeapSecondsFuture;
+ mWeekNumberLeapSecondsFuture = builder.mWeekNumberLeapSecondsFuture;
+ mDayNumberLeapSecondsFuture = builder.mDayNumberLeapSecondsFuture;
+ }
+
+ /** Returns the time difference due to leap seconds before the event in seconds. (UTC) */
+ @IntRange(from = 0)
+ public int getLeapSeconds() {
+ return mLeapSeconds;
+ }
+
+ /** Returns the time difference due to leap seconds after the event in seconds. (UTC) */
+ @IntRange(from = 0)
+ public int getLeapSecondsFuture() {
+ return mLeapSecondsFuture;
+ }
+
+ /** Returns the GNSS week number in which the leap second event will occur. (UTC) */
+ @IntRange(from = 0)
+ public int getWeekNumberLeapSecondsFuture() {
+ return mWeekNumberLeapSecondsFuture;
+ }
+
+ /** Returns the day number when the next leap second will occur. */
+ @IntRange(from = 0)
+ public int getDayNumberLeapSecondsFuture() {
+ return mDayNumberLeapSecondsFuture;
+ }
+
+ public static final @NonNull Creator<LeapSecondsModel> CREATOR =
+ new Creator<LeapSecondsModel>() {
+ @Override
+ @NonNull
+ public LeapSecondsModel createFromParcel(Parcel in) {
+ final LeapSecondsModel.Builder leapSecondsModel = new Builder();
+ leapSecondsModel.setLeapSeconds(in.readInt());
+ leapSecondsModel.setLeapSecondsFuture(in.readInt());
+ leapSecondsModel.setWeekNumberLeapSecondsFuture(in.readInt());
+ leapSecondsModel.setDayNumberLeapSecondsFuture(in.readInt());
+ return leapSecondsModel.build();
+ }
+
+ @Override
+ public LeapSecondsModel[] newArray(int size) {
+ return new LeapSecondsModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mLeapSeconds);
+ parcel.writeInt(mLeapSecondsFuture);
+ parcel.writeInt(mWeekNumberLeapSecondsFuture);
+ parcel.writeInt(mDayNumberLeapSecondsFuture);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("LeapSecondsModel[");
+ builder.append("leapSeconds = ").append(mLeapSeconds);
+ builder.append(", leapSecondsFuture = ").append(mLeapSecondsFuture);
+ builder.append(", weekNumberLeapSecondsFuture = ").append(mWeekNumberLeapSecondsFuture);
+ builder.append(", dayNumberLeapSecondsFuture = ").append(mDayNumberLeapSecondsFuture);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link LeapSecondsModel} */
+ public static final class Builder {
+ private int mLeapSeconds;
+ private int mLeapSecondsFuture;
+ private int mWeekNumberLeapSecondsFuture;
+ private int mDayNumberLeapSecondsFuture;
+
+ /** Sets the time difference due to leap seconds before the event in seconds. (UTC) */
+ @NonNull
+ public Builder setLeapSeconds(@IntRange(from = 0) int leapSeconds) {
+ mLeapSeconds = leapSeconds;
+ return this;
+ }
+
+ /** Sets the time difference due to leap seconds after the event in seconds. (UTC) */
+ @NonNull
+ public Builder setLeapSecondsFuture(@IntRange(from = 0) int leapSecondsFuture) {
+ mLeapSecondsFuture = leapSecondsFuture;
+ return this;
+ }
+
+ /** Sets the GNSS week number in which the leap second event will occur. (UTC) */
+ @NonNull
+ public Builder setWeekNumberLeapSecondsFuture(
+ @IntRange(from = 0) int weekNumberLeapSecondsFuture) {
+ mWeekNumberLeapSecondsFuture = weekNumberLeapSecondsFuture;
+ return this;
+ }
+
+ /** Sets the day number when the next leap second will occur. */
+ @NonNull
+ public Builder setDayNumberLeapSecondsFuture(
+ @IntRange(from = 0) int dayNumberLeapSecondsFuture) {
+ mDayNumberLeapSecondsFuture = dayNumberLeapSecondsFuture;
+ return this;
+ }
+
+ /** Builds a {@link LeapSecondsModel} instance as specified by this builder. */
+ @NonNull
+ public LeapSecondsModel build() {
+ return new LeapSecondsModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/QzssAssistance.java b/location/java/android/location/QzssAssistance.java
new file mode 100644
index 000000000000..9383ce3c63b5
--- /dev/null
+++ b/location/java/android/location/QzssAssistance.java
@@ -0,0 +1,282 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains QZSS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class QzssAssistance implements Parcelable {
+
+ /** The QZSS almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of QZSS ephemeris. */
+ @NonNull private final List<QzssSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of QZSS satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private QzssAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the QZSS almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of QZSS ephemeris. */
+ @NonNull
+ public List<QzssSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of QZSS satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ public static final @NonNull Creator<QzssAssistance> CREATOR =
+ new Creator<QzssAssistance>() {
+ @Override
+ @NonNull
+ public QzssAssistance createFromParcel(Parcel in) {
+ return new QzssAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(QzssSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+ @Override
+ public QzssAssistance[] newArray(int size) {
+ return new QzssAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("QzssAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link QzssAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<QzssSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the QZSS almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of QZSS ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<QzssSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity model. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of QZSS satellite correction. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds a {@link QzssAssistance} instance as specified by this builder. */
+ @NonNull
+ public QzssAssistance build() {
+ return new QzssAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/QzssSatelliteEphemeris.java b/location/java/android/location/QzssSatelliteEphemeris.java
new file mode 100644
index 000000000000..96203d9588c8
--- /dev/null
+++ b/location/java/android/location/QzssSatelliteEphemeris.java
@@ -0,0 +1,229 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.GpsSatelliteEphemeris.GpsL2Params;
+import android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel;
+import android.location.GpsSatelliteEphemeris.GpsSatelliteHealth;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to QZSS satellites.
+ *
+ * <p>This is defined in IS-QZSS-PNT section 4.1.2.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class QzssSatelliteEphemeris implements Parcelable {
+ /** Satellite PRN. */
+ private final int mPrn;
+
+ /** L2 parameters. */
+ @NonNull private final GpsL2Params mGpsL2Params;
+
+ /** Clock model. */
+ @NonNull private final GpsSatelliteClockModel mSatelliteClockModel;
+
+ /** Orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GpsSatelliteHealth mSatelliteHealth;
+
+ /** Ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 183, to = 206)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the L2 parameters of the satellite. */
+ @NonNull
+ public GpsL2Params getGpsL2Params() {
+ return mGpsL2Params;
+ }
+
+ /** Returns the clock model of the satellite. */
+ @NonNull
+ public GpsSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the orbit model of the satellite. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GpsSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mGpsL2Params, flags);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ private QzssSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mGpsL2Params, "GpsL2Params cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mGpsL2Params = builder.mGpsL2Params;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<QzssSatelliteEphemeris> CREATOR =
+ new Creator<QzssSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public QzssSatelliteEphemeris createFromParcel(Parcel in) {
+ final QzssSatelliteEphemeris.Builder qzssSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
+ .setSatelliteClockModel(
+ in.readTypedObject(GpsSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(GpsSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return qzssSatelliteEphemeris.build();
+ }
+
+ @Override
+ public QzssSatelliteEphemeris[] newArray(int size) {
+ return new QzssSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("QzssSatelliteEphemeris[");
+ builder.append("prn=").append(mPrn);
+ builder.append(", gpsL2Params=").append(mGpsL2Params);
+ builder.append(", satelliteClockModel=").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel=").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth=").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime=").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link QzssSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mPrn;
+ private GpsL2Params mGpsL2Params;
+ private GpsSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GpsSatelliteHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(@IntRange(from = 183, to = 206) int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the L2 parameters of the satellite. */
+ @NonNull
+ public Builder setGpsL2Params(@NonNull GpsL2Params gpsL2Params) {
+ mGpsL2Params = gpsL2Params;
+ return this;
+ }
+
+ /** Sets the clock model of the satellite. */
+ @NonNull
+ public Builder setSatelliteClockModel(@NonNull GpsSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the orbit model of the satellite. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GpsSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link QzssSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public QzssSatelliteEphemeris build() {
+ return new QzssSatelliteEphemeris(this);
+ }
+ }
+}
diff --git a/location/java/android/location/RealTimeIntegrityModel.java b/location/java/android/location/RealTimeIntegrityModel.java
new file mode 100644
index 000000000000..d268926e56e2
--- /dev/null
+++ b/location/java/android/location/RealTimeIntegrityModel.java
@@ -0,0 +1,284 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains the real time integrity status of a GNSS satellite based on notice advisory.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class RealTimeIntegrityModel implements Parcelable {
+ /**
+ * Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ private final int mSvid;
+
+ /** Indicates whether the satellite is currently usable for navigation. */
+ private final boolean mUsable;
+
+ /** UTC timestamp (in seconds) when the advisory was published. */
+ private final long mPublishDateSeconds;
+
+ /** UTC timestamp (in seconds) for the start of the event. */
+ private final long mStartDateSeconds;
+
+ /** UTC timestamp (in seconds) for the end of the event. */
+ private final long mEndDateSeconds;
+
+ /**
+ * Abbreviated type of the advisory, providing a concise summary of the event.
+ *
+ * <p>This field follows different definitions depending on the GNSS constellation:
+ * <p> - GPS: See NANU type definitions(https://www.navcen.uscg.gov/nanu-abbreviations-and-descriptions)
+ * <p> - Galileo: See NAGU type definitions(https://www.gsc-europa.eu/system-service-status/nagu-information)
+ * <p> - QZSS: See NAQU type definitions](https://sys.qzss.go.jp/dod/en/naqu/type.html)
+ * <p> - BeiDou: Not used; set to an empty string.
+ */
+ @NonNull private final String mAdvisoryType;
+
+ /**
+ * Unique identifier for the advisory within its constellation's system.
+ *
+ * <p>For BeiDou, this is not used and should be an empty string.
+ */
+ @NonNull private final String mAdvisoryNumber;
+
+ private RealTimeIntegrityModel(Builder builder) {
+ // Allow SV ID beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
+ Preconditions.checkArgument(builder.mPublishDateSeconds > 0);
+ Preconditions.checkArgument(builder.mStartDateSeconds > 0);
+ Preconditions.checkArgument(builder.mEndDateSeconds > 0);
+ Preconditions.checkNotNull(builder.mAdvisoryType, "AdvisoryType cannot be null");
+ Preconditions.checkNotNull(builder.mAdvisoryNumber, "AdvisoryNumber cannot be null");
+ mSvid = builder.mSvid;
+ mUsable = builder.mUsable;
+ mPublishDateSeconds = builder.mPublishDateSeconds;
+ mStartDateSeconds = builder.mStartDateSeconds;
+ mEndDateSeconds = builder.mEndDateSeconds;
+ mAdvisoryType = builder.mAdvisoryType;
+ mAdvisoryNumber = builder.mAdvisoryNumber;
+ }
+
+ /**
+ * Returns the Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /** Returns whether the satellite is usable or not. */
+ public boolean isUsable() {
+ return mUsable;
+ }
+
+ /** Returns the UTC timestamp (in seconds) when the advisory was published */
+ @IntRange(from = 0)
+ public long getPublishDateSeconds() {
+ return mPublishDateSeconds;
+ }
+
+ /** Returns UTC timestamp (in seconds) for the start of the event. */
+ @IntRange(from = 0)
+ public long getStartDateSeconds() {
+ return mStartDateSeconds;
+ }
+
+ /** Returns UTC timestamp (in seconds) for the end of the event. */
+ @IntRange(from = 0)
+ public long getEndDateSeconds() {
+ return mEndDateSeconds;
+ }
+
+ /** Returns the abbreviated type of notice advisory. */
+ @NonNull
+ public String getAdvisoryType() {
+ return mAdvisoryType;
+ }
+
+ /** Returns the unique identifier for the advisory. */
+ @NonNull
+ public String getAdvisoryNumber() {
+ return mAdvisoryNumber;
+ }
+
+ public static final @NonNull Creator<RealTimeIntegrityModel> CREATOR =
+ new Creator<RealTimeIntegrityModel>() {
+ @Override
+ @NonNull
+ public RealTimeIntegrityModel createFromParcel(Parcel in) {
+ RealTimeIntegrityModel realTimeIntegrityModel =
+ new RealTimeIntegrityModel.Builder()
+ .setSvid(in.readInt())
+ .setUsable(in.readBoolean())
+ .setPublishDateSeconds(in.readLong())
+ .setStartDateSeconds(in.readLong())
+ .setEndDateSeconds(in.readLong())
+ .setAdvisoryType(in.readString8())
+ .setAdvisoryNumber(in.readString8())
+ .build();
+ return realTimeIntegrityModel;
+ }
+
+ @Override
+ public RealTimeIntegrityModel[] newArray(int size) {
+ return new RealTimeIntegrityModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvid);
+ parcel.writeBoolean(mUsable);
+ parcel.writeLong(mPublishDateSeconds);
+ parcel.writeLong(mStartDateSeconds);
+ parcel.writeLong(mEndDateSeconds);
+ parcel.writeString8(mAdvisoryType);
+ parcel.writeString8(mAdvisoryNumber);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("RealTimeIntegrityModel[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", usable = ").append(mUsable);
+ builder.append(", publishDateSeconds = ").append(mPublishDateSeconds);
+ builder.append(", startDateSeconds = ").append(mStartDateSeconds);
+ builder.append(", endDateSeconds = ").append(mEndDateSeconds);
+ builder.append(", advisoryType = ").append(mAdvisoryType);
+ builder.append(", advisoryNumber = ").append(mAdvisoryNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link RealTimeIntegrityModel} */
+ public static final class Builder {
+ private int mSvid;
+ private boolean mUsable;
+ private long mPublishDateSeconds;
+ private long mStartDateSeconds;
+ private long mEndDateSeconds;
+ private String mAdvisoryType;
+ private String mAdvisoryNumber;
+
+ /**
+ * Sets the Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ @NonNull
+ public Builder setSvid(@IntRange(from = 1, to = 206) int svid) {
+ mSvid = svid;
+ return this;
+ }
+
+ /** Sets whether the satellite is usable or not. */
+ @NonNull
+ public Builder setUsable(boolean usable) {
+ mUsable = usable;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) when the advisory was published. */
+ @NonNull
+ public Builder setPublishDateSeconds(@IntRange(from = 0) long publishDateSeconds) {
+ mPublishDateSeconds = publishDateSeconds;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) for the start of the event. */
+ @NonNull
+ public Builder setStartDateSeconds(@IntRange(from = 0) long startDateSeconds) {
+ mStartDateSeconds = startDateSeconds;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) for the end of the event. */
+ @NonNull
+ public Builder setEndDateSeconds(@IntRange(from = 0) long endDateSeconds) {
+ mEndDateSeconds = endDateSeconds;
+ return this;
+ }
+
+ /** Sets the abbreviated type of notice advisory. */
+ @NonNull
+ public Builder setAdvisoryType(@NonNull String advisoryType) {
+ mAdvisoryType = advisoryType;
+ return this;
+ }
+
+ /** Sets the unique identifier for the advisory. */
+ @NonNull
+ public Builder setAdvisoryNumber(@NonNull String advisoryNumber) {
+ mAdvisoryNumber = advisoryNumber;
+ return this;
+ }
+
+ /** Builds a {@link RealTimeIntegrityModel} instance as specified by this builder. */
+ @NonNull
+ public RealTimeIntegrityModel build() {
+ return new RealTimeIntegrityModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/SatelliteEphemerisTime.java b/location/java/android/location/SatelliteEphemerisTime.java
new file mode 100644
index 000000000000..0ab3acfe3147
--- /dev/null
+++ b/location/java/android/location/SatelliteEphemerisTime.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains time of ephemeris for GPS, Galileo, and QZSS.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200, section 20.3.3.4.1.
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD, section 5.1.2, 5.1.9.2.
+ * <p>For QZSS, this is defined in IS-QZSS-200, section 4.1.2.4.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class SatelliteEphemerisTime implements Parcelable {
+ /** The issue of ephemeris data. */
+ private final int mIode;
+
+ /** The satellite week number without rollover. */
+ private final int mWeekNumber;
+
+ /** The broadcast time of ephemeris in GNSS time of week in seconds. */
+ private final int mToeSeconds;
+
+ private SatelliteEphemerisTime(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mIode, 0, 1023, "Iode");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604799, "ToeSeconds");
+ mIode = builder.mIode;
+ mWeekNumber = builder.mWeekNumber;
+ mToeSeconds = builder.mToeSeconds;
+ }
+
+ /** Returns the issue of ephemeris data. */
+ @IntRange(from = 0, to = 1023)
+ public int getIode() {
+ return mIode;
+ }
+
+ /** Returns the satellite week number without rollover. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ /** Returns the broadcast time of ephemeris in GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604799)
+ public int getToeSeconds() {
+ return mToeSeconds;
+ }
+
+ public static final @NonNull Creator<SatelliteEphemerisTime> CREATOR =
+ new Creator<SatelliteEphemerisTime>() {
+ @Override
+ public SatelliteEphemerisTime createFromParcel(Parcel in) {
+ final SatelliteEphemerisTime.Builder satelliteEphemerisTime =
+ new Builder()
+ .setIode(in.readInt())
+ .setWeekNumber(in.readInt())
+ .setToeSeconds(in.readInt());
+ return satelliteEphemerisTime.build();
+ }
+
+ @Override
+ public SatelliteEphemerisTime[] newArray(int size) {
+ return new SatelliteEphemerisTime[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mIode);
+ parcel.writeInt(mWeekNumber);
+ parcel.writeInt(mToeSeconds);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("SatelliteEphemerisTime[");
+ builder.append("iode = ").append(mIode);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append(", toeSeconds = ").append(mToeSeconds);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link SatelliteEphemerisTime}. */
+ public static final class Builder {
+ private int mIode;
+ private int mWeekNumber;
+ private int mToeSeconds;
+
+ /** Sets the issue of ephemeris data. */
+ @NonNull
+ public Builder setIode(@IntRange(from = 0, to = 1023) int iode) {
+ mIode = iode;
+ return this;
+ }
+
+ /** Sets the satellite week number without rollover. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Sets the broadcast time of ephemeris in GNSS time of week in seconds. */
+ @NonNull
+ public Builder setToeSeconds(@IntRange(from = 0, to = 604799) int toeSeconds) {
+ mToeSeconds = toeSeconds;
+ return this;
+ }
+
+ /** Builds a {@link SatelliteEphemerisTime} instance as specified by this builder. */
+ @NonNull
+ public SatelliteEphemerisTime build() {
+ return new SatelliteEphemerisTime(this);
+ }
+ }
+}
diff --git a/location/java/android/location/TimeModel.java b/location/java/android/location/TimeModel.java
new file mode 100644
index 000000000000..380f7b87d8f6
--- /dev/null
+++ b/location/java/android/location/TimeModel.java
@@ -0,0 +1,208 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.GnssStatus.ConstellationType;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains the GNSS-GNSS system time offset between the GNSS system time.
+ *
+ * <p>This is defined in IS-GPS-200 section 30.3.3.8.2.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class TimeModel implements Parcelable {
+ /*
+ * Model represents parameters to convert from current GNSS to GNSS system
+ * time indicated by toGnss.
+ */
+ private final @ConstellationType int mToGnss;
+
+ /** Bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ private final double mA0;
+
+ /** Drift coefficient of GNSS time scale relative to GNSS time scale in seconds per second. */
+ private final double mA1;
+
+ /** GNSS time of week in seconds. */
+ private final int mTimeOfWeek;
+
+ /** Week number of the GNSS time. */
+ private final int mWeekNumber;
+
+ private TimeModel(Builder builder) {
+ Preconditions.checkArgumentInRange(
+ builder.mToGnss,
+ GnssStatus.CONSTELLATION_UNKNOWN,
+ GnssStatus.CONSTELLATION_COUNT,
+ "ToGnss");
+ Preconditions.checkArgumentInRange(builder.mA0, -1.0f, 1.0f, "A0");
+ Preconditions.checkArgumentInRange(builder.mA1, -3.28e-6f, 3.28e-6f, "A1");
+ Preconditions.checkArgumentInRange(builder.mTimeOfWeek, 0, 604800, "TimeOfWeek");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ mToGnss = builder.mToGnss;
+ mA0 = builder.mA0;
+ mA1 = builder.mA1;
+ mTimeOfWeek = builder.mTimeOfWeek;
+ mWeekNumber = builder.mWeekNumber;
+ }
+
+ /** Returns the constellation type to convert from current GNSS system time. */
+ @ConstellationType
+ public int getToGnss() {
+ return mToGnss;
+ }
+
+ /** Returns the bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getA0() {
+ return mA0;
+ }
+
+ /**
+ * Returns the drift coefficient of GNSS time scale relative to GNSS time scale in seconds per
+ * second.
+ */
+ @FloatRange(from = -3.28e-6f, to = 3.28e-6f)
+ public double getA1() {
+ return mA1;
+ }
+
+ /** Returns the GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getTimeOfWeek() {
+ return mTimeOfWeek;
+ }
+
+ /** Returns the week number of the GNSS time. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ public static final @NonNull Creator<TimeModel> CREATOR =
+ new Creator<TimeModel>() {
+ @Override
+ public TimeModel createFromParcel(@NonNull Parcel source) {
+ return new TimeModel.Builder()
+ .setToGnss(source.readInt())
+ .setA0(source.readDouble())
+ .setA1(source.readDouble())
+ .setTimeOfWeek(source.readInt())
+ .setWeekNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public TimeModel[] newArray(int size) {
+ return new TimeModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("TimeModel[");
+ builder.append("toGnss = ").append(mToGnss);
+ builder.append(", a0 = ").append(mA0);
+ builder.append(", a1 = ").append(mA1);
+ builder.append(", timeOfWeek = ").append(mTimeOfWeek);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mToGnss);
+ dest.writeDouble(mA0);
+ dest.writeDouble(mA1);
+ dest.writeInt(mTimeOfWeek);
+ dest.writeInt(mWeekNumber);
+ }
+
+ /** Builder for {@link TimeModel} */
+ public static final class Builder {
+
+ private @ConstellationType int mToGnss;
+ private double mA0;
+ private double mA1;
+ private int mTimeOfWeek;
+ private int mWeekNumber;
+
+ /** Sets the constellation type to convert from current GNSS system time. */
+ @NonNull
+ public Builder setToGnss(@ConstellationType int toGnss) {
+ mToGnss = toGnss;
+ return this;
+ }
+
+ /** Sets the bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ @NonNull
+ public Builder setA0(@FloatRange(from = -1.0f, to = 1.0f) double a0) {
+ mA0 = a0;
+ return this;
+ }
+
+ /**
+ * Sets the drift coefficient of GNSS time scale relative to GNSS time scale in seconds per
+ * second.
+ */
+ @NonNull
+ public Builder setA1(@FloatRange(from = -3.28e-6f, to = 3.28e-6f) double a1) {
+ mA1 = a1;
+ return this;
+ }
+
+ /** Sets the GNSS time of week in seconds. */
+ @NonNull
+ public Builder setTimeOfWeek(@IntRange(from = 0, to = 604800) int timeOfWeek) {
+ mTimeOfWeek = timeOfWeek;
+ return this;
+ }
+
+ /** Sets the week number of the GNSS time. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Builds the {@link TimeModel} object. */
+ @NonNull
+ public TimeModel build() {
+ return new TimeModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/UtcModel.java b/location/java/android/location/UtcModel.java
new file mode 100644
index 000000000000..6dc633de3fcb
--- /dev/null
+++ b/location/java/android/location/UtcModel.java
@@ -0,0 +1,176 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains parameters to convert from current GNSS time to UTC time.
+ *
+ * <p>This is defined in RINEX 3.05 "TIME SYSTEM CORR" in table A5.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class UtcModel implements Parcelable {
+ /** Bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ private final double mA0;
+
+ /** Drift coefficient of GNSS time scale relative to UTC time scale in seconds per second. */
+ private final double mA1;
+
+ /** Reference GNSS time of week in seconds. */
+ private final int mTimeOfWeek;
+
+ /** Reference GNSS week number. */
+ private final int mWeekNumber;
+
+ private UtcModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mA0, -2.0f, 2.0f, "A0");
+ Preconditions.checkArgumentInRange(builder.mA1, -7.45e-9f, 7.45e-9f, "A1");
+ Preconditions.checkArgumentInRange(builder.mTimeOfWeek, 0, 604800, "TimeOfWeek");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ mA0 = builder.mA0;
+ mA1 = builder.mA1;
+ mTimeOfWeek = builder.mTimeOfWeek;
+ mWeekNumber = builder.mWeekNumber;
+ }
+
+ /** Returns the bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ @FloatRange(from = -2.0f, to = 2.0f)
+ public double getA0() {
+ return mA0;
+ }
+
+ /**
+ * Returns the drift coefficient of GNSS time scale relative to UTC time scale in seconds per
+ * second.
+ */
+ @FloatRange(from = -7.45e-9f, to = 7.45e-9f)
+ public double getA1() {
+ return mA1;
+ }
+
+ /** Returns the reference GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getTimeOfWeek() {
+ return mTimeOfWeek;
+ }
+
+ /** Returns the reference GNSS week number. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("UtcModel[");
+ builder.append("a0 = ").append(mA0);
+ builder.append(", a1 = ").append(mA1);
+ builder.append(", timeOfWeek = ").append(mTimeOfWeek);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mA0);
+ dest.writeDouble(mA1);
+ dest.writeInt(mTimeOfWeek);
+ dest.writeInt(mWeekNumber);
+ }
+
+ public static final @NonNull Creator<UtcModel> CREATOR =
+ new Creator<UtcModel>() {
+ @Override
+ public UtcModel createFromParcel(@NonNull Parcel source) {
+ return new UtcModel.Builder()
+ .setA0(source.readDouble())
+ .setA1(source.readDouble())
+ .setTimeOfWeek(source.readInt())
+ .setWeekNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public UtcModel[] newArray(int size) {
+ return new UtcModel[size];
+ }
+ };
+
+ /** Builder for {@link UtcModel}. */
+ public static final class Builder {
+ private double mA0;
+ private double mA1;
+ private int mTimeOfWeek;
+ private int mWeekNumber;
+
+ /** Sets the bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ @NonNull
+ public Builder setA0(@FloatRange(from = -2.0f, to = 2.0f) double a0) {
+ mA0 = a0;
+ return this;
+ }
+
+ /**
+ * Sets the drift coefficient of GNSS time scale relative to UTC time scale in seconds per
+ * second.
+ */
+ @NonNull
+ public Builder setA1(@FloatRange(from = -7.45e-9f, to = 7.45e-9f) double a1) {
+ mA1 = a1;
+ return this;
+ }
+
+ /** Sets the reference GNSS time of week in seconds. */
+ @NonNull
+ public Builder setTimeOfWeek(@IntRange(from = 0, to = 604800) int timeOfWeek) {
+ mTimeOfWeek = timeOfWeek;
+ return this;
+ }
+
+ /** Sets the reference GNSS week number. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Builds a {@link UtcModel} instance as specified by this builder. */
+ @NonNull
+ public UtcModel build() {
+ return new UtcModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 5395206155b3..c02cc808d60c 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -161,3 +161,10 @@ flag {
description: "Flag for gating the density-based coarse locations"
bug: "376198890"
}
+
+flag {
+ name: "gnss_assistance_interface"
+ namespace: "location"
+ description: "Flag for GNSS assistance interface"
+ bug: "209078566"
+} \ No newline at end of file
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index b41f40f412d2..5689df0784f0 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -548,8 +548,45 @@ public final class AudioDeviceInfo {
return counts;
}
+ /** @hide */
+ @IntDef(flag = true, prefix = "AudioFormat.CHANNEL_OUT_", value = {
+ AudioFormat.CHANNEL_INVALID,
+ AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.CHANNEL_OUT_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY,
+ AudioFormat.CHANNEL_OUT_BACK_LEFT,
+ AudioFormat.CHANNEL_OUT_BACK_RIGHT,
+ AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER,
+ AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER,
+ AudioFormat.CHANNEL_OUT_BACK_CENTER,
+ AudioFormat.CHANNEL_OUT_SIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_SIDE_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2,
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(FLAG_SPEAKER_LAYOUT_API)
+ public @interface SpeakerLayoutChannelMask {}
+
/**
- * @return A ChannelMask representing the physical output speaker layout of the device.
+ * @return A ChannelMask representing the speaker layout of a TYPE_BUILTIN_SPEAKER device.
+ *
+ * Valid only for speakers built-in to the device.
*
* The layout channel mask only indicates which speaker channels are present, the
* physical layout of the speakers should be informed by a standard for multi-channel
@@ -557,6 +594,7 @@ public final class AudioDeviceInfo {
* @see AudioFormat
*/
@FlaggedApi(FLAG_SPEAKER_LAYOUT_API)
+ @SpeakerLayoutChannelMask
public int getSpeakerLayoutChannelMask() {
return mPort.speakerLayoutChannelMask();
}
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 4b3962e6dd74..9e9bbeea0635 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -210,6 +210,27 @@ public class AudioDevicePort extends AudioPort {
return super.equals(o);
}
+ /**
+ * Returns true if the AudioDevicePort passed as argument represents the same device (same
+ * type and same address). This is different from equals() in that the port IDs are not compared
+ * which allows matching devices across native audio server restarts.
+ * @param other the other audio device port to compare to.
+ * @return true if both device port correspond to the same audio device, false otherwise.
+ * @hide
+ */
+ public boolean isSameAs(AudioDevicePort other) {
+ if (mType != other.type()) {
+ return false;
+ }
+ if (mAddress == null && other.address() != null) {
+ return false;
+ }
+ if (!mAddress.equals(other.address())) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String toString() {
String type = (mRole == ROLE_SOURCE ?
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index c72a74efcff8..8bc66a048d27 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -17,6 +17,7 @@
package android.media;
import static android.media.audio.Flags.FLAG_DOLBY_AC4_LEVEL4_ENCODING_API;
+import static android.media.audio.Flags.FLAG_IAMF_DEFINITIONS_API;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -382,6 +383,103 @@ public final class AudioFormat implements Parcelable {
@FlaggedApi(FLAG_DOLBY_AC4_LEVEL4_ENCODING_API)
public static final int ENCODING_AC4_L4 = 32;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_OPUS = 33;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_AAC = 34;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_FLAC = 35;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_PCM = 36;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_OPUS = 37;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_AAC = 38;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_FLAC = 39;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_PCM = 40;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS = 41;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC = 42;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC = 43;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM = 44;
+
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
switch(enc) {
@@ -449,6 +547,30 @@ public final class AudioFormat implements Parcelable {
return "ENCODING_DTS_UHD_P2";
case ENCODING_DSD:
return "ENCODING_DSD";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM";
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ return "ENCODING_IAMF_BASE_PROFILE_AAC";
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ return "ENCODING_IAMF_BASE_PROFILE_FLAC";
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ return "ENCODING_IAMF_BASE_PROFILE_OPUS";
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ return "ENCODING_IAMF_BASE_PROFILE_PCM";
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_AAC";
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_FLAC";
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_OPUS";
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_PCM";
default :
return "invalid encoding " + enc;
}
@@ -931,6 +1053,18 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return true;
default:
return false;
@@ -972,6 +1106,18 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return true;
default:
return false;
@@ -1015,6 +1161,18 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DRA:
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM: // PCM but inside compressed stream
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM: // PCM but inside compressed stream
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM: // PCM but inside compressed stream
return false;
case ENCODING_INVALID:
default:
@@ -1058,6 +1216,18 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DRA:
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return false;
case ENCODING_INVALID:
default:
@@ -1350,6 +1520,18 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1580,7 +1762,19 @@ public final class AudioFormat implements Parcelable {
ENCODING_DRA,
ENCODING_DTS_HD_MA,
ENCODING_DTS_UHD_P2,
- ENCODING_DSD }
+ ENCODING_DSD,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM,
+ ENCODING_IAMF_BASE_PROFILE_AAC,
+ ENCODING_IAMF_BASE_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_PROFILE_PCM,
+ ENCODING_IAMF_SIMPLE_PROFILE_AAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_FLAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_OPUS,
+ ENCODING_IAMF_SIMPLE_PROFILE_PCM }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1619,7 +1813,19 @@ public final class AudioFormat implements Parcelable {
ENCODING_DRA,
ENCODING_DTS_HD_MA,
ENCODING_DTS_UHD_P2,
- ENCODING_DSD }
+ ENCODING_DSD,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM,
+ ENCODING_IAMF_BASE_PROFILE_AAC,
+ ENCODING_IAMF_BASE_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_PROFILE_PCM,
+ ENCODING_IAMF_SIMPLE_PROFILE_AAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_FLAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_OPUS,
+ ENCODING_IAMF_SIMPLE_PROFILE_PCM }
)
@Retention(RetentionPolicy.SOURCE)
public @interface EncodingCanBeInvalid {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9beeef4c160f..52eae43f7db9 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8068,15 +8068,15 @@ public class AudioManager {
ArrayList<AudioDevicePort> ports_A, ArrayList<AudioDevicePort> ports_B, int flags) {
ArrayList<AudioDevicePort> delta_ports = new ArrayList<AudioDevicePort>();
-
- AudioDevicePort cur_port = null;
for (int cur_index = 0; cur_index < ports_B.size(); cur_index++) {
boolean cur_port_found = false;
- cur_port = ports_B.get(cur_index);
+ AudioDevicePort cur_port = ports_B.get(cur_index);
for (int prev_index = 0;
prev_index < ports_A.size() && !cur_port_found;
prev_index++) {
- cur_port_found = (cur_port.id() == ports_A.get(prev_index).id());
+ // Do not compare devices by port ID as these change when the native
+ // audio server restarts
+ cur_port_found = cur_port.isSameAs(ports_A.get(prev_index));
}
if (!cur_port_found) {
@@ -8422,13 +8422,10 @@ public class AudioManager {
* Callback method called when the mediaserver dies
*/
public void onServiceDied() {
- synchronized (mDeviceCallbacks) {
- broadcastDeviceListChange_sync(null);
- }
+ // Nothing to do here
}
}
-
/**
* @hide
* Abstract class to receive event notification about audioserver process state.
@@ -8469,9 +8466,13 @@ public class AudioManager {
/**
* @hide
* Registers a callback for notification of audio server state changes.
- * @param executor {@link Executor} to handle the callbacks
- * @param stateCallback the callback to receive the audio server state changes
- * To remove the callabck, pass a null reference for both executor and stateCallback.
+ * @param executor {@link Executor} to handle the callbacks. Must be non null.
+ * @param stateCallback the callback to receive the audio server state changes.
+ * Must be non null. To remove the callabck,
+ * call {@link #clearAudioServerStateCallback()}
+ * @throws IllegalArgumentException If a null argument is specified.
+ * @throws IllegalStateException If a callback is already registered
+ * *
*/
@SystemApi
public void setAudioServerStateCallback(@NonNull Executor executor,
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 763eb2944d19..5685710e5f15 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -97,17 +97,15 @@ class AudioPortEventHandler {
ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
- if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
- int status = AudioManager.updateAudioPortCache(ports, patches, null);
- if (status != AudioManager.SUCCESS) {
- // Since audio ports and audio patches are not null, the return
- // value could be ERROR due to inconsistency between port generation
- // and patch generation. In this case, we need to reschedule the
- // message to make sure the native callback is done.
- sendMessageDelayed(obtainMessage(msg.what, msg.obj),
- RESCHEDULE_MESSAGE_DELAY_MS);
- return;
- }
+ int status = AudioManager.updateAudioPortCache(ports, patches, null);
+ if (status != AudioManager.SUCCESS) {
+ // Since audio ports and audio patches are not null, the return
+ // value could be ERROR due to inconsistency between port generation
+ // and patch generation. In this case, we need to reschedule the
+ // message to make sure the native callback is done.
+ sendMessageDelayed(obtainMessage(msg.what, msg.obj),
+ RESCHEDULE_MESSAGE_DELAY_MS);
+ return;
}
switch (msg.what) {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d0d91ba599f9..12d7f33a0d51 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -559,6 +559,30 @@ public class AudioSystem
return "AUDIO_FORMAT_MPEGH_SUB_LC_L3";
case /* AUDIO_FORMAT_MPEGH_SUB_LC_L4 */ 0x2C000024:
return "AUDIO_FORMAT_MPEGH_SUB_LC_L4";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_OPUS */ 0x34010001:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_OPUS";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_AAC */ 0x34010002:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_AAC";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_FLAC */ 0x34010004:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_FLAC";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_PCM */ 0x34010008:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_PCM";
+ case /* AUDIO_FORMAT_IAMF_BASE_OPUS */ 0x34020001:
+ return "AUDIO_FORMAT_IAMF_BASE_OPUS";
+ case /* AUDIO_FORMAT_IAMF_BASE_AAC */ 0x34020002:
+ return "AUDIO_FORMAT_IAMF_BASE_AAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_FLAC */ 0x34020004:
+ return "AUDIO_FORMAT_IAMF_BASE_FLAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_PCM */ 0x34020008:
+ return "AUDIO_FORMAT_IAMF_BASE_PCM";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS */ 0x34040001:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC */ 0x34040002:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC */ 0x34040004:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM */ 0x34040008:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM";
default:
return "AUDIO_FORMAT_(" + audioFormat + ")";
}
diff --git a/media/java/android/media/IMediaRoute2ProviderService.aidl b/media/java/android/media/IMediaRoute2ProviderService.aidl
index eee3d22c7252..714cacb8d326 100644
--- a/media/java/android/media/IMediaRoute2ProviderService.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderService.aidl
@@ -33,6 +33,23 @@ oneway interface IMediaRoute2ProviderService {
void requestCreateSession(long requestId, String packageName, String routeId,
in @nullable Bundle sessionHints);
+ /**
+ * Requests the creation of a system media routing session.
+ *
+ * @param requestId The id of the request.
+ * @param uid The uid of the package whose media to route, or
+ * {@link android.os.Process#INVALID_UID} if routing should not be restricted to a specific
+ * uid.
+ * @param packageName The name of the package whose media to route.
+ * @param routeId The id of the route to be initially selected.
+ * @param sessionHints An optional bundle with parameters.
+ */
+ void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ in @nullable Bundle sessionHints);
void selectRoute(long requestId, String sessionId, String routeId);
void deselectRoute(long requestId, String sessionId, String routeId);
void transferToRoute(long requestId, String sessionId, String routeId);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 50387548b4ab..bcb70019b3ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -53,7 +53,7 @@ import java.util.stream.Collectors;
* The format of the media data is specified as key/value pairs. Keys are strings. Values can
* be integer, long, float, String or ByteBuffer.
* <p>
- * The feature metadata is specificed as string/boolean pairs.
+ * The feature metadata is specified as string/boolean pairs.
* <p>
* Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
*
@@ -1244,12 +1244,12 @@ public final class MediaFormat {
/**
* An optional key describing the desired encoder latency in frames. This is an optional
- * parameter that applies only to video encoders. If encoder supports it, it should ouput
+ * parameter that applies only to video encoders. If encoder supports it, it should output
* at least one output frame after being queued the specified number of frames. This key
* is ignored if the video encoder does not support the latency feature. Use the output
* format to verify that this feature was enabled and the actual value used by the encoder.
* <p>
- * If the key is not specified, the default latency will be implenmentation specific.
+ * If the key is not specified, the default latency will be implementation specific.
* The associated value is an integer.
*/
public static final String KEY_LATENCY = "latency";
@@ -1507,16 +1507,16 @@ public final class MediaFormat {
*/
public static final String KEY_COLOR_STANDARD = "color-standard";
- /** BT.709 color chromacity coordinates with KR = 0.2126, KB = 0.0722. */
+ /** BT.709 color chromaticity coordinates with KR = 0.2126, KB = 0.0722. */
public static final int COLOR_STANDARD_BT709 = 1;
- /** BT.601 625 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+ /** BT.601 625 color chromaticity coordinates with KR = 0.299, KB = 0.114. */
public static final int COLOR_STANDARD_BT601_PAL = 2;
- /** BT.601 525 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+ /** BT.601 525 color chromaticity coordinates with KR = 0.299, KB = 0.114. */
public static final int COLOR_STANDARD_BT601_NTSC = 4;
- /** BT.2020 color chromacity coordinates with KR = 0.2627, KB = 0.0593. */
+ /** BT.2020 color chromaticity coordinates with KR = 0.2627, KB = 0.0593. */
public static final int COLOR_STANDARD_BT2020 = 6;
/** @hide */
@@ -2150,7 +2150,7 @@ public final class MediaFormat {
* Sets the value of a string key.
* <p>
* If value is {@code null}, it sets a null value that behaves similarly to a missing key.
- * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * This could be used prior to API level {@link android.os.Build.VERSION_CODES#Q} to effectively
* remove a key.
*/
public final void setString(@NonNull String name, @Nullable String value) {
@@ -2161,7 +2161,7 @@ public final class MediaFormat {
* Sets the value of a ByteBuffer key.
* <p>
* If value is {@code null}, it sets a null value that behaves similarly to a missing key.
- * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * This could be used prior to API level {@link android.os.Build.VERSION_CODES#Q} to effectively
* remove a key.
*/
public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dd5067a3ee67..bbb03e77c8c9 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -21,6 +21,7 @@ import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
import static com.android.media.flags.Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
@@ -49,6 +50,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Describes the properties of a route.
@@ -189,6 +191,7 @@ public final class MediaRoute2Info implements Parcelable {
* the device.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BUILTIN_SPEAKER
*/
public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
@@ -196,6 +199,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a headset, which is the combination of a headphones and a microphone.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_WIRED_HEADSET
*/
public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
@@ -203,6 +207,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a pair of wired headphones.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_WIRED_HEADPHONES
*/
public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
@@ -210,6 +215,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a bluetooth device, such as a bluetooth speaker or headphones.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BLUETOOTH_A2DP
*/
public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -217,6 +223,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI
*/
public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
@@ -224,6 +231,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is an Audio Return Channel of an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI_ARC
*/
@FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public static final int TYPE_HDMI_ARC = AudioDeviceInfo.TYPE_HDMI_ARC;
@@ -232,24 +240,34 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is an Enhanced Audio Return Channel of an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI_EARC
*/
@FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC;
/**
* Indicates the route is a digital line connection (for example S/PDIF).
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_LINE_DIGITAL
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_LINE_DIGITAL = AudioDeviceInfo.TYPE_LINE_DIGITAL;
/**
* Indicates the route is an analog line-level connection.
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_LINE_ANALOG
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_LINE_ANALOG = AudioDeviceInfo.TYPE_LINE_ANALOG;
/**
* Indicates the route is using the auxiliary line-level connectors.
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_AUX_LINE
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_AUX_LINE = AudioDeviceInfo.TYPE_AUX_LINE;
@@ -258,6 +276,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a USB audio device.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_DEVICE
*/
public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
@@ -265,6 +284,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a USB audio device in accessory mode.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_ACCESSORY
*/
public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
@@ -272,6 +292,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is the audio device associated with a dock.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_DOCK
*/
public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
@@ -279,6 +300,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a USB audio headset.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_HEADSET
*/
public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
@@ -286,6 +308,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a hearing aid.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HEARING_AID
*/
public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
@@ -293,6 +316,7 @@ public final class MediaRoute2Info implements Parcelable {
* Indicates the route is a Bluetooth Low Energy (BLE) HEADSET.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BLE_HEADSET
*/
public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
@@ -304,6 +328,7 @@ public final class MediaRoute2Info implements Parcelable {
* to provide a better experience on multichannel contents.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_MULTICHANNEL_GROUP
*/
@FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE)
public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP =
@@ -607,7 +632,7 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
private final int mConnectionState;
private final String mClientPackageName;
- private final String mPackageName;
+ private final String mProviderPackageName;
@PlaybackVolume private final int mVolumeHandling;
private final int mVolumeMax;
private final int mVolume;
@@ -617,7 +642,7 @@ public final class MediaRoute2Info implements Parcelable {
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
- private final Set<String> mRequiredPermissions;
+ private final List<Set<String>> mRequiredPermissions;
@SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
@@ -631,7 +656,7 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
mClientPackageName = builder.mClientPackageName;
- mPackageName = builder.mPackageName;
+ mProviderPackageName = builder.mProviderPackageName;
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
@@ -642,7 +667,7 @@ public final class MediaRoute2Info implements Parcelable {
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
mSuitabilityStatus = builder.mSuitabilityStatus;
- mRequiredPermissions = Set.copyOf(builder.mRequiredPermissions);
+ mRequiredPermissions = List.copyOf(builder.mRequiredPermissions);
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -657,7 +682,7 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
mClientPackageName = in.readString();
- mPackageName = in.readString();
+ mProviderPackageName = in.readString();
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
@@ -667,7 +692,12 @@ public final class MediaRoute2Info implements Parcelable {
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
- mRequiredPermissions = Set.of(in.createString8Array());
+ ArrayList<Set<String>> requiredPermissions = new ArrayList<>();
+ int numRequiredPermissionSets = in.readInt();
+ for (int i = 0; i < numRequiredPermissionSets; i++) {
+ requiredPermissions.add(Set.of(in.createString8Array()));
+ }
+ mRequiredPermissions = List.copyOf(requiredPermissions); // Use copyOf to make it immutable.
mSuitabilityStatus = in.readInt();
}
@@ -772,14 +802,19 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
- * Gets the package name of the provider that published the route.
- * <p>
- * It is set by the system service.
- * @hide
+ * Gets the package name of the {@link MediaRoute2ProviderService provider} that published the
+ * route, or null if it has not yet been populated.
+ *
+ * <p>The package name of the route provider is populated by the system as part of {@link
+ * MediaRoute2ProviderService#notifyRoutes(java.util.Collection)}. As a result, it's expectable
+ * that a {@link MediaRoute2Info} instance that hasn't yet been published will have a null
+ * provider package name. Otherwise, routes obtained via {@link MediaRouter2} should have a
+ * populated provider package name.
*/
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
@Nullable
- public String getPackageName() {
- return mPackageName;
+ public String getProviderPackageName() {
+ return mProviderPackageName;
}
/**
@@ -888,6 +923,26 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Returns whether this route supports routing of the system media.
+ *
+ * @hide
+ */
+ public boolean supportsSystemMediaRouting() {
+ return (mRoutingTypeFlags
+ & (FLAG_ROUTING_TYPE_SYSTEM_VIDEO | FLAG_ROUTING_TYPE_SYSTEM_AUDIO))
+ != 0;
+ }
+
+ /**
+ * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}.
+ *
+ * @hide
+ */
+ public boolean supportsRemoteRouting() {
+ return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0;
+ }
+
+ /**
* Returns true if the route info has all of the required field.
* A route is valid if and only if it is obtained from
* {@link com.android.server.media.MediaRouterService}.
@@ -903,19 +958,23 @@ public final class MediaRoute2Info implements Parcelable {
/**
* Returns whether this route is visible to the package with the given name.
+ *
* @hide
*/
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
public boolean isVisibleTo(String packageName) {
- return !mIsVisibilityRestricted || getPackageName().equals(packageName)
+ return !mIsVisibilityRestricted
+ || TextUtils.equals(getProviderPackageName(), packageName)
|| mAllowedPackages.contains(packageName);
}
/**
- * @return the set of permissions which must be held to see this route
+ * @return a list of permission sets - all the permissions in at least one of these sets must be
+ * held to see this route.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
- public Set<String> getRequiredPermissions() {
+ public List<Set<String>> getRequiredPermissions() {
return mRequiredPermissions;
}
@@ -979,7 +1038,7 @@ public final class MediaRoute2Info implements Parcelable {
pw.println(indent + "mDescription=" + mDescription);
pw.println(indent + "mConnectionState=" + mConnectionState);
pw.println(indent + "mClientPackageName=" + mClientPackageName);
- pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mProviderPackageName=" + mProviderPackageName);
dumpVolume(pw, indent);
@@ -1018,7 +1077,7 @@ public final class MediaRoute2Info implements Parcelable {
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
- && Objects.equals(mPackageName, other.mPackageName)
+ && Objects.equals(mProviderPackageName, other.mProviderPackageName)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
@@ -1045,7 +1104,7 @@ public final class MediaRoute2Info implements Parcelable {
mDescription,
mConnectionState,
mClientPackageName,
- mPackageName,
+ mProviderPackageName,
mVolumeHandling,
mVolumeMax,
mVolume,
@@ -1096,7 +1155,8 @@ public final class MediaRoute2Info implements Parcelable {
.append(", allowedPackages=")
.append(String.join(",", mAllowedPackages))
.append(", mRequiredPermissions=")
- .append(String.join(",", mRequiredPermissions))
+ .append(mRequiredPermissions.stream().map(set -> String.join(",", set)).collect(
+ Collectors.joining("),(", "(", ")")))
.append(", suitabilityStatus=")
.append(mSuitabilityStatus)
.append(" }")
@@ -1120,7 +1180,7 @@ public final class MediaRoute2Info implements Parcelable {
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
dest.writeString(mClientPackageName);
- dest.writeString(mPackageName);
+ dest.writeString(mProviderPackageName);
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
@@ -1130,7 +1190,10 @@ public final class MediaRoute2Info implements Parcelable {
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
- dest.writeString8Array(mRequiredPermissions.toArray(new String[0]));
+ dest.writeInt(mRequiredPermissions.size());
+ for (Set<String> permissionSet : mRequiredPermissions) {
+ dest.writeString8Array(permissionSet.toArray(new String[0]));
+ }
dest.writeInt(mSuitabilityStatus);
}
@@ -1269,7 +1332,7 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
private int mConnectionState;
private String mClientPackageName;
- private String mPackageName;
+ private String mProviderPackageName;
@PlaybackVolume private int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
private int mVolumeMax;
private int mVolume;
@@ -1279,7 +1342,7 @@ public final class MediaRoute2Info implements Parcelable {
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
- private Set<String> mRequiredPermissions;
+ private List<Set<String>> mRequiredPermissions;
@SuitabilityStatus private int mSuitabilityStatus;
/**
@@ -1305,7 +1368,7 @@ public final class MediaRoute2Info implements Parcelable {
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
- mRequiredPermissions = Set.of();
+ mRequiredPermissions = List.of();
}
/**
@@ -1342,7 +1405,7 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
mClientPackageName = routeInfo.mClientPackageName;
- mPackageName = routeInfo.mPackageName;
+ mProviderPackageName = routeInfo.mProviderPackageName;
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
@@ -1489,11 +1552,13 @@ public final class MediaRoute2Info implements Parcelable {
/**
* Sets the package name of the route.
+ *
* @hide
*/
+ // It is set by the MediaRouterService.
@NonNull
- public Builder setPackageName(@NonNull String packageName) {
- mPackageName = packageName;
+ public Builder setProviderPackageName(@NonNull String providerPackageName) {
+ mProviderPackageName = providerPackageName;
return this;
}
@@ -1587,7 +1652,7 @@ public final class MediaRoute2Info implements Parcelable {
public Builder setVisibilityPublic() {
mIsVisibilityRestricted = false;
mAllowedPackages = Set.of();
- mRequiredPermissions = Set.of();
+ mRequiredPermissions = List.of();
return this;
}
@@ -1614,13 +1679,31 @@ public final class MediaRoute2Info implements Parcelable {
/**
* Limits the visibility of this route to holders of a set of permissions.
*
+ * <p>Calls to this method override any previous calls of
+ * {@link #setRequiredPermissions(Set)} or {@link #setRequiredPermissions(List)}.
+ *
* @param requiredPermissions the list of all permissions which must be held in order to
* see this route.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
public Builder setRequiredPermissions(@NonNull Set<String> requiredPermissions) {
- mRequiredPermissions = Set.copyOf(requiredPermissions);
+ return setRequiredPermissions(List.of(requiredPermissions));
+ }
+
+ /**
+ * Limits the visibility of this route to holders of one of a set of permissions.
+ *
+ * <p>Calls to this method override any previous calls of
+ * {@link #setRequiredPermissions(Set)} or {@link #setRequiredPermissions(List)}.
+ *
+ * @param requiresOneOf a list of Sets of permissions. Holding all permissions in at least
+ * one of the Sets is required for the route to be visible.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Builder setRequiredPermissions(@NonNull List<Set<String>> requiresOneOf) {
+ mRequiredPermissions = List.copyOf(requiresOneOf);
return this;
}
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index 809ee23a2b2e..bcc8cbbc2b91 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -152,11 +152,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
/**
* Sets the package name and unique id of the provider info.
- * <p>
- * The unique id is automatically set by
- * {@link com.android.server.media.MediaRouterService} and used to identify providers.
- * The id set by {@link MediaRoute2ProviderService} will be ignored.
- * </p>
+ *
+ * <p>The unique id is automatically set by {@link
+ * com.android.server.media.MediaRouterService} and used to identify providers. The id set
+ * by {@link MediaRoute2ProviderService} will be ignored.
+ *
* @hide
*/
@NonNull
@@ -168,10 +168,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>();
for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) {
- MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
- .setPackageName(packageName)
- .setProviderId(mUniqueId)
- .build();
+ MediaRoute2Info routeWithProviderId =
+ new MediaRoute2Info.Builder(entry.getValue())
+ .setProviderPackageName(packageName)
+ .setProviderId(mUniqueId)
+ .build();
newRoutes.put(routeWithProviderId.getOriginalId(), routeWithProviderId);
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 547099f044b2..09f40e005b4c 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -18,14 +18,21 @@ package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static java.util.Objects.requireNonNull;
+
+import android.Manifest;
import android.annotation.CallSuper;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.Intent;
+import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioMixingRule;
+import android.media.audiopolicy.AudioPolicy;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -36,6 +43,7 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.LongSparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;
@@ -47,7 +55,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -83,8 +90,7 @@ public abstract class MediaRoute2ProviderService extends Service {
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
/**
- * {@link Intent} action that indicates that the declaring service supports routing of the
- * system media.
+ * A category that indicates that the declaring service supports routing of the system media.
*
* <p>Providers must include this action if they intend to publish routes that support the
* system media, as described by {@link MediaRoute2Info#getSupportedRoutingTypes()}.
@@ -94,7 +100,7 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
// TODO: b/362507305 - Unhide once the implementation and CTS are in place.
@FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY)
public static final String SERVICE_INTERFACE_SYSTEM_MEDIA =
"android.media.MediaRoute2ProviderService.SYSTEM_MEDIA";
@@ -165,6 +171,16 @@ public abstract class MediaRoute2ProviderService extends Service {
@FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
public static final int REASON_UNIMPLEMENTED = 5;
+ /**
+ * The request has failed because the provider has failed to route system media.
+ *
+ * @see #notifyRequestFailed
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public static final int REASON_FAILED_TO_REROUTE_SYSTEM_MEDIA = 6;
+
/** @hide */
@IntDef(
prefix = "REASON_",
@@ -174,7 +190,8 @@ public abstract class MediaRoute2ProviderService extends Service {
REASON_NETWORK_ERROR,
REASON_ROUTE_NOT_AVAILABLE,
REASON_INVALID_COMMAND,
- REASON_UNIMPLEMENTED
+ REASON_UNIMPLEMENTED,
+ REASON_FAILED_TO_REROUTE_SYSTEM_MEDIA
})
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
@@ -187,15 +204,28 @@ public abstract class MediaRoute2ProviderService extends Service {
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private final AtomicBoolean mSessionUpdateScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
+ /** Populated by system_server in {@link #setCallback}. Monotonically non-null. */
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
private volatile MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mRequestIdsLock")
private final Deque<Long> mRequestIds = new ArrayDeque<>(MAX_REQUEST_IDS_SIZE);
+ /**
+ * Maps system media session creation request ids to a package uid whose media to route. The
+ * value may be {@link Process#INVALID_UID} for routing sessions that don't affect a specific
+ * package (for example, if they affect the entire system).
+ */
+ @GuardedBy("mRequestIdsLock")
+ private final LongSparseArray<Integer> mSystemMediaSessionCreationRequests =
+ new LongSparseArray<>();
+
@GuardedBy("mSessionLock")
private final ArrayMap<String, RoutingSessionInfo> mSessionInfos = new ArrayMap<>();
+ @GuardedBy("mSessionLock")
+ private final ArrayMap<String, MediaStreams> mOngoingMediaStreams = new ArrayMap<>();
+
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -282,7 +312,7 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
public final void notifySessionCreated(long requestId,
@NonNull RoutingSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ requireNonNull(sessionInfo, "sessionInfo must not be null");
if (DEBUG) {
Log.d(TAG, "notifySessionCreated: Creating a session. requestId=" + requestId
@@ -326,17 +356,129 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param formats the {@link MediaStreamsFormats} that describes the format for the {@link
* MediaStreams} to return.
* @return a {@link MediaStreams} instance that holds the media streams to route as part of the
- * newly created routing session.
+ * newly created routing session. May be null if system media capture failed, in which case
+ * you can ignore the return value, as you will receive a call to {@link #onReleaseSession}
+ * where you can clean up this session
* @hide
*/
// TODO: b/362507305 - Unhide once the implementation and CTS are in place.
@FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
- @NonNull
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Nullable
public final MediaStreams notifySystemMediaSessionCreated(
long requestId,
@NonNull RoutingSessionInfo sessionInfo,
@NonNull MediaStreamsFormats formats) {
- throw new UnsupportedOperationException();
+ requireNonNull(sessionInfo, "sessionInfo must not be null");
+ requireNonNull(formats, "formats must not be null");
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "notifySystemMediaSessionCreated: Creating a session. requestId="
+ + requestId
+ + ", sessionInfo="
+ + sessionInfo);
+ }
+
+ Integer uid;
+ synchronized (mRequestIdsLock) {
+ uid = mSystemMediaSessionCreationRequests.get(requestId);
+ mSystemMediaSessionCreationRequests.remove(requestId);
+ }
+
+ if (uid == null) {
+ throw new IllegalStateException(
+ "Unexpected system routing session created (request id="
+ + requestId
+ + "):"
+ + sessionInfo);
+ }
+
+ if (mRemoteCallback == null) {
+ throw new IllegalStateException("Unexpected: remote callback is null.");
+ }
+
+ int routingTypes = 0;
+ var providerInfo = mProviderInfo;
+ for (String selectedRouteId : sessionInfo.getSelectedRoutes()) {
+ MediaRoute2Info route = providerInfo.mRoutes.get(selectedRouteId);
+ if (route == null) {
+ throw new IllegalArgumentException(
+ "Invalid selected route with id: " + selectedRouteId);
+ }
+ routingTypes |= route.getSupportedRoutingTypes();
+ }
+
+ if ((routingTypes & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_AUDIO) == 0) {
+ // TODO: b/380431086 - Populate video stream once we add support for video.
+ throw new IllegalArgumentException(
+ "Selected routes for system media don't support any system media routing"
+ + " types.");
+ }
+
+ AudioFormat audioFormat = formats.mAudioFormat;
+ var mediaStreamsBuilder = new MediaStreams.Builder();
+ if (audioFormat != null) {
+ populateAudioStream(audioFormat, uid, mediaStreamsBuilder);
+ }
+ // TODO: b/380431086 - Populate video stream once we add support for video.
+
+ MediaStreams streams = mediaStreamsBuilder.build();
+ var audioRecord = streams.mAudioRecord;
+ if (audioRecord == null) {
+ Log.e(
+ TAG,
+ "Audio record is not populated. Returning an empty stream and scheduling the"
+ + " session release for: "
+ + sessionInfo);
+ mHandler.post(() -> onReleaseSession(REQUEST_ID_NONE, sessionInfo.getOriginalId()));
+ notifyRequestFailed(requestId, REASON_FAILED_TO_REROUTE_SYSTEM_MEDIA);
+ return null;
+ }
+
+ synchronized (mSessionLock) {
+ try {
+ mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ mOngoingMediaStreams.put(sessionInfo.getOriginalId(), streams);
+ return streams;
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ private void populateAudioStream(
+ AudioFormat audioFormat, int uid, MediaStreams.Builder builder) {
+ var audioAttributes =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+ var audioMixingRuleBuilder =
+ new AudioMixingRule.Builder()
+ .addRule(audioAttributes, AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
+ if (uid != Process.INVALID_UID) {
+ audioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid);
+ }
+
+ AudioMix mix =
+ new AudioMix.Builder(audioMixingRuleBuilder.build())
+ .setFormat(audioFormat)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK)
+ .build();
+ AudioPolicy audioPolicy =
+ new AudioPolicy.Builder(this).setLooper(mHandler.getLooper()).addMix(mix).build();
+ var audioManager = getSystemService(AudioManager.class);
+ if (audioManager == null) {
+ Log.e(TAG, "Couldn't fetch the audio manager.");
+ return;
+ }
+ audioManager.registerAudioPolicy(audioPolicy);
+ var audioRecord = audioPolicy.createAudioRecordSink(mix);
+ if (audioRecord == null) {
+ Log.e(TAG, "Audio record creation failed.");
+ audioManager.unregisterAudioPolicy(audioPolicy);
+ return;
+ }
+ builder.setAudioStream(audioPolicy, audioRecord);
}
/**
@@ -344,7 +486,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed.
*/
public final void notifySessionUpdated(@NonNull RoutingSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ requireNonNull(sessionInfo, "sessionInfo must not be null");
if (DEBUG) {
Log.d(TAG, "notifySessionUpdated: Updating session id=" + sessionInfo);
@@ -379,6 +521,7 @@ public abstract class MediaRoute2ProviderService extends Service {
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfos.remove(sessionId);
+ maybeReleaseMediaStreams(sessionId);
if (sessionInfo == null) {
Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
@@ -396,6 +539,34 @@ public abstract class MediaRoute2ProviderService extends Service {
}
}
+ /** Releases any system media routing resources associated with the given {@code sessionId}. */
+ private void maybeReleaseMediaStreams(String sessionId) {
+ if (!Flags.enableMirroringInMediaRouter2()) {
+ return;
+ }
+ synchronized (mSessionLock) {
+ var streams = mOngoingMediaStreams.remove(sessionId);
+ if (streams != null) {
+ releaseAudioStream(streams.mAudioPolicy, streams.mAudioRecord);
+ // TODO: b/380431086: Release the video stream once implemented.
+ }
+ }
+ }
+
+ // We cannot reach the code that requires MODIFY_AUDIO_ROUTING without holding it.
+ @SuppressWarnings("MissingPermission")
+ private void releaseAudioStream(AudioPolicy audioPolicy, AudioRecord audioRecord) {
+ if (audioPolicy == null) {
+ return;
+ }
+ var audioManager = getSystemService(AudioManager.class);
+ if (audioManager == null) {
+ return;
+ }
+ audioRecord.stop();
+ audioManager.unregisterAudioPolicy(audioPolicy);
+ }
+
/**
* Notifies to the client that the request has failed.
*
@@ -569,7 +740,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* Updates routes of the provider and notifies the system media router service.
*/
public final void notifyRoutes(@NonNull Collection<MediaRoute2Info> routes) {
- Objects.requireNonNull(routes, "routes must not be null");
+ requireNonNull(routes, "routes must not be null");
List<MediaRoute2Info> sanitizedRoutes = new ArrayList<>(routes.size());
for (MediaRoute2Info route : routes) {
@@ -763,6 +934,32 @@ public abstract class MediaRoute2ProviderService extends Service {
}
@Override
+ public void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
+ return;
+ }
+ synchronized (mRequestIdsLock) {
+ mSystemMediaSessionCreationRequests.put(requestId, uid);
+ }
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRoute2ProviderService::onCreateSystemRoutingSession,
+ MediaRoute2ProviderService.this,
+ requestId,
+ packageName,
+ routeId,
+ sessionHints));
+ }
+
+ @Override
public void selectRoute(long requestId, String sessionId, String routeId) {
if (!checkCallerIsSystem()) {
return;
@@ -825,6 +1022,10 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
+ // We proactively release the system media routing once the system requests it, to
+ // ensure it happens immediately.
+ maybeReleaseMediaStreams(sessionId);
+
addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
MediaRoute2ProviderService.this, requestId, sessionId));
@@ -843,12 +1044,14 @@ public abstract class MediaRoute2ProviderService extends Service {
@FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
public static final class MediaStreams {
- private final AudioRecord mAudioRecord;
+ @Nullable private final AudioPolicy mAudioPolicy;
+ @Nullable private final AudioRecord mAudioRecord;
// TODO: b/380431086: Add the video equivalent.
- private MediaStreams(AudioRecord mAudioRecord) {
- this.mAudioRecord = mAudioRecord;
+ private MediaStreams(Builder builder) {
+ this.mAudioPolicy = builder.mAudioPolicy;
+ this.mAudioRecord = builder.mAudioRecord;
}
/**
@@ -859,8 +1062,33 @@ public abstract class MediaRoute2ProviderService extends Service {
public AudioRecord getAudioRecord() {
return mAudioRecord;
}
+
+ /**
+ * Builder for {@link MediaStreams}.
+ *
+ * @hide
+ */
+ public static final class Builder {
+
+ @Nullable private AudioPolicy mAudioPolicy;
+ @Nullable private AudioRecord mAudioRecord;
+
+ /** Populates system media audio-related structures. */
+ public Builder setAudioStream(
+ @NonNull AudioPolicy audioPolicy, @NonNull AudioRecord audioRecord) {
+ mAudioPolicy = requireNonNull(audioPolicy);
+ mAudioRecord = requireNonNull(audioRecord);
+ return this;
+ }
+
+ /** Builds a {@link MediaStreams} instance. */
+ public MediaStreams build() {
+ return new MediaStreams(this);
+ }
+ }
}
+
/**
* Holds the formats to encode media data to be read from {@link MediaStreams}.
*
@@ -911,7 +1139,7 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
@NonNull
public Builder setAudioFormat(@NonNull AudioFormat audioFormat) {
- this.mAudioFormat = Objects.requireNonNull(audioFormat);
+ this.mAudioFormat = requireNonNull(audioFormat);
return this;
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 20108e7369d7..245360c925ad 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -19,6 +19,7 @@ package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL;
import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING;
@@ -809,7 +810,7 @@ public final class MediaRouter2 {
* updates} in order to keep the system UI in a consistent state. You can also call this method
* at any other point to update the listing preference dynamically.
*
- * <p>Any calls to this method from a privileged router will throw an {@link
+ * <p>Calling this method on a proxy router instance will throw an {@link
* UnsupportedOperationException}.
*
* <p>Notes:
@@ -1398,6 +1399,7 @@ public final class MediaRouter2 {
requestCreateController(controller, route, managerRequestId);
}
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
private List<MediaRoute2Info> getSortedRoutes(
List<MediaRoute2Info> routes, List<String> packageOrder) {
if (packageOrder.isEmpty()) {
@@ -1412,11 +1414,13 @@ public final class MediaRouter2 {
ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes);
// take the negative for descending order
sortedRoutes.sort(
- Comparator.comparingInt(r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+ Comparator.comparingInt(
+ r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0)));
return sortedRoutes;
}
@GuardedBy("mLock")
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
private List<MediaRoute2Info> filterRoutesWithCompositePreferenceLocked(
List<MediaRoute2Info> routes) {
@@ -1429,10 +1433,10 @@ public final class MediaRouter2 {
continue;
}
if (!mDiscoveryPreference.getAllowedPackages().isEmpty()
- && (route.getPackageName() == null
+ && (route.getProviderPackageName() == null
|| !mDiscoveryPreference
.getAllowedPackages()
- .contains(route.getPackageName()))) {
+ .contains(route.getProviderPackageName()))) {
continue;
}
if (mDiscoveryPreference.shouldRemoveDuplicates()) {
@@ -2675,7 +2679,7 @@ public final class MediaRouter2 {
@Override
public void setRouteListingPreference(@Nullable RouteListingPreference preference) {
throw new UnsupportedOperationException(
- "RouteListingPreference cannot be set by a privileged MediaRouter2 instance.");
+ "RouteListingPreference cannot be set by a proxy MediaRouter2 instance.");
}
@Override
@@ -3643,6 +3647,7 @@ public final class MediaRouter2 {
}
}
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
@Override
public List<MediaRoute2Info> filterRoutesWithIndividualPreference(
List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryPreference) {
@@ -3652,10 +3657,10 @@ public final class MediaRouter2 {
continue;
}
if (!discoveryPreference.getAllowedPackages().isEmpty()
- && (route.getPackageName() == null
+ && (route.getProviderPackageName() == null
|| !discoveryPreference
.getAllowedPackages()
- .contains(route.getPackageName()))) {
+ .contains(route.getProviderPackageName()))) {
continue;
}
filteredRoutes.add(route);
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 7e1dccf2d366..3854747f46e0 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -20,9 +20,11 @@ import static android.media.MediaRouter2.SCANNING_STATE_NOT_SCANNING;
import static android.media.MediaRouter2.SCANNING_STATE_WHILE_INTERACTIVE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -285,6 +287,7 @@ public final class MediaRouter2Manager {
(route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute());
}
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) {
if (!preference.shouldRemoveDuplicates()) {
synchronized (mRoutesLock) {
@@ -302,12 +305,15 @@ public final class MediaRouter2Manager {
routes = new ArrayList<>(mRoutes.values());
}
// take the negative for descending order
- routes.sort(Comparator.comparingInt(
- r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+ routes.sort(
+ Comparator.comparingInt(
+ r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0)));
return routes;
}
- private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo,
+ @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
+ private List<MediaRoute2Info> getFilteredRoutes(
+ @NonNull RoutingSessionInfo sessionInfo,
boolean includeSelectedRoutes,
@Nullable Predicate<MediaRoute2Info> additionalFilter) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
@@ -336,9 +342,10 @@ public final class MediaRouter2Manager {
continue;
}
if (!discoveryPreference.getAllowedPackages().isEmpty()
- && (route.getPackageName() == null
- || !discoveryPreference.getAllowedPackages()
- .contains(route.getPackageName()))) {
+ && (route.getProviderPackageName() == null
+ || !discoveryPreference
+ .getAllowedPackages()
+ .contains(route.getProviderPackageName()))) {
continue;
}
if (additionalFilter != null && !additionalFilter.test(route)) {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index bbe8e4ed7b34..4398b261377b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -60,6 +60,13 @@ flag {
}
flag {
+ name: "enable_media_route_2_info_provider_package_name"
+ namespace: "media_better_together"
+ description: "Enables a new API to obtain the provider package name from MediaRoute2Info."
+ bug: "378788958"
+}
+
+flag {
name: "enable_mirroring_in_media_router_2"
namespace: "media_better_together"
description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
diff --git a/media/java/android/media/quality/ActiveProcessingPicture.aidl b/media/java/android/media/quality/ActiveProcessingPicture.aidl
new file mode 100644
index 000000000000..2851306f6e4d
--- /dev/null
+++ b/media/java/android/media/quality/ActiveProcessingPicture.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.quality;
+
+parcelable ActiveProcessingPicture; \ No newline at end of file
diff --git a/media/java/android/media/quality/ActiveProcessingPicture.java b/media/java/android/media/quality/ActiveProcessingPicture.java
new file mode 100644
index 000000000000..e16ad62e23f2
--- /dev/null
+++ b/media/java/android/media/quality/ActiveProcessingPicture.java
@@ -0,0 +1,86 @@
+/*
+ * 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 android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Active picture represents an image or video undergoing picture processing which uses a picture
+ * profile. The picture profile is used to configure the picture processing parameters.
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class ActiveProcessingPicture implements Parcelable {
+ private final int mId;
+ private final String mProfileId;
+
+ public ActiveProcessingPicture(int id, @NonNull String profileId) {
+ mId = id;
+ mProfileId = profileId;
+ }
+
+ /** @hide */
+ ActiveProcessingPicture(Parcel in) {
+ mId = in.readInt();
+ mProfileId = in.readString();
+ }
+
+ @NonNull
+ public static final Creator<ActiveProcessingPicture> CREATOR = new Creator<>() {
+ @Override
+ public ActiveProcessingPicture createFromParcel(Parcel in) {
+ return new ActiveProcessingPicture(in);
+ }
+
+ @Override
+ public ActiveProcessingPicture[] newArray(int size) {
+ return new ActiveProcessingPicture[size];
+ }
+ };
+
+ /**
+ * An ID that uniquely identifies the active content.
+ *
+ * <p>The ID is assigned by the system to distinguish different active contents.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * The ID of the picture profile used to configure the content.
+ */
+ @NonNull
+ public String getProfileId() {
+ return mProfileId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mProfileId);
+ }
+}
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index 70211caf9380..253c2d896d63 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -23,51 +23,58 @@ import android.media.quality.ISoundProfileCallback;
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfileHandle;
import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfileHandle;
import android.media.quality.SoundProfile;
+import android.os.UserHandle;
/**
* Interface for Media Quality Manager
* @hide
*/
interface IMediaQualityManager {
- PictureProfile createPictureProfile(in PictureProfile pp, int userId);
- void updatePictureProfile(in String id, in PictureProfile pp, int userId);
- void removePictureProfile(in String id, int userId);
- PictureProfile getPictureProfile(in int type, in String name, int userId);
- List<PictureProfile> getPictureProfilesByPackage(in String packageName, int userId);
- List<PictureProfile> getAvailablePictureProfiles(int userId);
- boolean setDefaultPictureProfile(in String id, int userId);
- List<String> getPictureProfilePackageNames(int userId);
- List<String> getPictureProfileAllowList(int userId);
- void setPictureProfileAllowList(in List<String> packages, int userId);
- PictureProfileHandle getPictureProfileHandle(in String id, int userId);
+ PictureProfile createPictureProfile(in PictureProfile pp, in UserHandle user);
+ void updatePictureProfile(in String id, in PictureProfile pp, in UserHandle user);
+ void removePictureProfile(in String id, in UserHandle user);
+ boolean setDefaultPictureProfile(in String id, in UserHandle user);
+ PictureProfile getPictureProfile(
+ in int type, in String name, in boolean includeParams, in UserHandle user);
+ List<PictureProfile> getPictureProfilesByPackage(
+ in String packageName, in boolean includeParams, in UserHandle user);
+ List<PictureProfile> getAvailablePictureProfiles(in boolean includeParams, in UserHandle user);
+ List<String> getPictureProfilePackageNames(in UserHandle user);
+ List<String> getPictureProfileAllowList(in UserHandle user);
+ void setPictureProfileAllowList(in List<String> packages, in UserHandle user);
+ List<PictureProfileHandle> getPictureProfileHandle(in String[] id, in UserHandle user);
- SoundProfile createSoundProfile(in SoundProfile pp, int userId);
- void updateSoundProfile(in String id, in SoundProfile pp, int userId);
- void removeSoundProfile(in String id, int userId);
- SoundProfile getSoundProfile(in int type, in String name, int userId);
- List<SoundProfile> getSoundProfilesByPackage(in String packageName, int userId);
- List<SoundProfile> getAvailableSoundProfiles(int userId);
- boolean setDefaultSoundProfile(in String id, int userId);
- List<String> getSoundProfilePackageNames(int userId);
- List<String> getSoundProfileAllowList(int userId);
- void setSoundProfileAllowList(in List<String> packages, int userId);
+ SoundProfile createSoundProfile(in SoundProfile pp, in UserHandle user);
+ void updateSoundProfile(in String id, in SoundProfile pp, in UserHandle user);
+ void removeSoundProfile(in String id, in UserHandle user);
+ boolean setDefaultSoundProfile(in String id, in UserHandle user);
+ SoundProfile getSoundProfile(
+ in int type, in String name, in boolean includeParams, in UserHandle user);
+ List<SoundProfile> getSoundProfilesByPackage(
+ in String packageName, in boolean includeParams, in UserHandle user);
+ List<SoundProfile> getAvailableSoundProfiles(in boolean includeParams, in UserHandle user);
+ List<String> getSoundProfilePackageNames(in UserHandle user);
+ List<String> getSoundProfileAllowList(in UserHandle user);
+ void setSoundProfileAllowList(in List<String> packages, in UserHandle user);
+ List<SoundProfileHandle> getSoundProfileHandle(in String[] id, in UserHandle user);
void registerPictureProfileCallback(in IPictureProfileCallback cb);
void registerSoundProfileCallback(in ISoundProfileCallback cb);
void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
- List<ParamCapability> getParamCapabilities(in List<String> names, int userId);
+ List<ParamCapability> getParamCapabilities(in List<String> names, in UserHandle user);
- boolean isSupported(int userId);
- void setAutoPictureQualityEnabled(in boolean enabled, int userId);
- boolean isAutoPictureQualityEnabled(int userId);
- void setSuperResolutionEnabled(in boolean enabled, int userId);
- boolean isSuperResolutionEnabled(int userId);
- void setAutoSoundQualityEnabled(in boolean enabled, int userId);
- boolean isAutoSoundQualityEnabled(int userId);
+ boolean isSupported(in UserHandle user);
+ void setAutoPictureQualityEnabled(in boolean enabled, in UserHandle user);
+ boolean isAutoPictureQualityEnabled(in UserHandle user);
+ void setSuperResolutionEnabled(in boolean enabled, in UserHandle user);
+ boolean isSuperResolutionEnabled(in UserHandle user);
+ void setAutoSoundQualityEnabled(in boolean enabled, in UserHandle user);
+ boolean isAutoSoundQualityEnabled(in UserHandle user);
- void setAmbientBacklightSettings(in AmbientBacklightSettings settings, int userId);
- void setAmbientBacklightEnabled(in boolean enabled, int userId);
- boolean isAmbientBacklightEnabled(int userId);
+ void setAmbientBacklightSettings(in AmbientBacklightSettings settings, in UserHandle user);
+ void setAmbientBacklightEnabled(in boolean enabled, in UserHandle user);
+ boolean isAmbientBacklightEnabled(in UserHandle user);
}
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl
index 34aa2b061caf..7071a1684fa2 100644
--- a/media/java/android/media/quality/IPictureProfileCallback.aidl
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -29,5 +29,5 @@ oneway interface IPictureProfileCallback {
void onPictureProfileUpdated(in String id, in PictureProfile p);
void onPictureProfileRemoved(in String id, in PictureProfile p);
void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
- void onError(in int err);
+ void onError(in String id, in int err);
}
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
index 9043757316bc..30bb106ef34c 100644
--- a/media/java/android/media/quality/ISoundProfileCallback.aidl
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -29,5 +29,5 @@ oneway interface ISoundProfileCallback {
void onSoundProfileUpdated(in String id, in SoundProfile p);
void onSoundProfileRemoved(in String id, in SoundProfile p);
void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
- void onError(in int err);
+ void onError(in String id, in int err);
}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 7b0bd04f3559..6a52bcba547a 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -75,11 +75,9 @@ public class MediaQualityContract {
public static final String PARAMETER_SATURATION = "saturation";
/**
- * @hide
- */
- public static final String PARAMETER_COLOR = "color";
- /**
- * @hide
+ * The hue.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_HUE = "hue";
@@ -89,47 +87,77 @@ public class MediaQualityContract {
public static final String PARAMETER_BACKLIGHT = "backlight";
/**
- * @hide
+ * Adjust brightness in advance color engine. Similar to a "brightness" control on a TV
+ * but acts at a lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
/**
- * @hide
+ * Adjust saturation in advance color engine. Similar to a "saturation" control on a TV
+ * but acts at a lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
/**
- * @hide
+ * Adjust hue in advance color engine. Similar to a "hue" control on a TV but acts at a
+ * lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
/**
- * @hide
+ * Advance setting for red offset. Adjust the black level of red color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and
+ * dark areas of the image.
+ *
+ * <p>Type: INTEGER
*/
- public static final String PARAMETER_COLOR_TUNER_REDO_FFSET = "color_tuner_red_offset";
+ public static final String PARAMETER_COLOR_TUNER_RED_OFFSET = "color_tuner_red_offset";
/**
- * @hide
+ * Advance setting for green offset. Adjust the black level of green color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and dark
+ * areas of the image.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
/**
- * @hide
+ * Advance setting for blue offset. Adjust the black level of blue color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and dark areas
+ * of the image.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
/**
- * @hide
+ * Advance setting for red gain. Adjust the gain or amplification of the red color channels.
+ * They control the overall intensity and white balance of red.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
/**
- * @hide
+ * Advance setting for green gain. Adjust the gain or amplification of the green color
+ * channels. They control the overall intensity and white balance of green.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
/**
- * @hide
+ * Advance setting for blue gain. Adjust the gain or amplification of the blue color
+ * channels.They control the overall intensity and white balance of blue.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
@@ -143,33 +171,54 @@ public class MediaQualityContract {
*/
public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution";
- /**
- * @hide
+ /** Noise reduction.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
/**
- * @hide
- */
+ * MPEG (moving picture experts group) noise reduction
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
+ * */
public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
/**
- * @hide
+ * Refine the flesh colors in the pictures without affecting the other colors on the screen.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_FLESH_TONE = "flesh_tone";
/**
- * @hide
+ * Contour noise reduction.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_DECONTOUR = "decontour";
/**
- * @hide
+ * Dynamically change picture luma to enhance contrast.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
/**
- * @hide
+ * Enable/disable film mode
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_FILM_MODE = "film_mode";
@@ -179,25 +228,50 @@ public class MediaQualityContract {
public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
/**
- * @hide
+ * Enable/disable blue color auto stretch
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
/**
- * @hide
+ * Enable/disable the overall color tuning feature.
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_COLOR_TUNE = "color_tune";
/**
- * @hide
+ * Adjust color temperature type
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
/**
- * @hide
+ * Enable/disable globe dimming.
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+ /**
+ * Enable/disable auto adjust picture parameter based on the TV content.
+ *
+ * <p>Type: BOOLEAN
+ */
+ public static final String PARAMETER_AUTO_PICTURE_QUALITY_ENABLED =
+ "auto_picture_quality_enabled";
+
+ /**
+ * Enable/disable auto upscaling the picture quality. It analyzes the lower-resolution
+ * image and uses its knowledge to invent the missing pixel, make the image look sharper.
+ *
+ * <p>Type: BOOLEAN
+ */
+ public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED =
+ "auto_super_resolution_enabled";
+
private PictureQuality() {
}
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 9d6608683fd7..7e87462b64de 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -20,11 +20,13 @@ import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.media.tv.flags.Flags;
import android.os.RemoteException;
+import android.os.UserHandle;
import androidx.annotation.RequiresPermission;
@@ -47,7 +49,7 @@ public final class MediaQualityManager {
private final IMediaQualityManager mService;
private final Context mContext;
- private final int mUserId;
+ private final UserHandle mUserHandle;
private final Object mLock = new Object();
// @GuardedBy("mLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
@@ -55,6 +57,9 @@ public final class MediaQualityManager {
private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
// @GuardedBy("mLock")
private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
+ // @GuardedBy("mLock")
+ private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
+ new ArrayList<>();
/**
@@ -62,7 +67,7 @@ public final class MediaQualityManager {
*/
public MediaQualityManager(Context context, IMediaQualityManager service) {
mContext = context;
- mUserId = context.getUserId();
+ mUserHandle = context.getUser();
mService = service;
IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
@@ -102,11 +107,11 @@ public final class MediaQualityManager {
}
}
@Override
- public void onError(int err) {
+ public void onError(String profileId, int err) {
synchronized (mLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
- record.postError(err);
+ record.postError(profileId, err);
}
}
}
@@ -149,11 +154,11 @@ public final class MediaQualityManager {
}
}
@Override
- public void onError(int err) {
+ public void onError(String profileId, int err) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
- record.postError(err);
+ record.postError(profileId, err);
}
}
}
@@ -210,18 +215,21 @@ public final class MediaQualityManager {
}
}
-
/**
* Gets picture profile by given profile type and name.
*
+ * @param type the type of the profile.
+ * @param name the name of the profile.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @return the corresponding picture profile if available; {@code null} if the name doesn't
- * exist.
+ * exist.
*/
@Nullable
public PictureProfile getPictureProfile(
- @PictureProfile.ProfileType int type, @NonNull String name) {
+ @PictureProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
try {
- return mService.getPictureProfile(type, name, mUserId);
+ return mService.getPictureProfile(type, name, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -231,14 +239,18 @@ public final class MediaQualityManager {
/**
* Gets profiles that available to the given package.
*
+ * @param packageName the package name of the profiles.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
- public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) {
+ public List<PictureProfile> getPictureProfilesByPackage(
+ @NonNull String packageName, boolean includeParams) {
try {
- return mService.getPictureProfilesByPackage(packageName, mUserId);
+ return mService.getPictureProfilesByPackage(packageName, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -246,11 +258,16 @@ public final class MediaQualityManager {
/**
* Gets profiles that available to the caller.
+ *
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ * @return the corresponding picture profile if available; {@code null} if the name doesn't
+ * exist.
*/
@NonNull
- public List<PictureProfile> getAvailablePictureProfiles() {
+ public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams) {
try {
- return mService.getAvailablePictureProfiles(mUserId);
+ return mService.getAvailablePictureProfiles(includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -268,7 +285,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public boolean setDefaultPictureProfile(@Nullable String id) {
try {
- return mService.setDefaultPictureProfile(id, mUserId);
+ return mService.setDefaultPictureProfile(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -277,7 +294,7 @@ public final class MediaQualityManager {
/**
* Gets all package names whose picture profiles are available.
*
- * @see #getPictureProfilesByPackage(String)
+ * @see #getPictureProfilesByPackage(String, boolean)
* @hide
*/
@SystemApi
@@ -285,7 +302,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<String> getPictureProfilePackageNames() {
try {
- return mService.getPictureProfilePackageNames(mUserId);
+ return mService.getPictureProfilePackageNames(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -295,9 +312,21 @@ public final class MediaQualityManager {
* Gets picture profile handle by profile ID.
* @hide
*/
- public PictureProfileHandle getPictureProfileHandle(String id) {
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] id) {
try {
- return mService.getPictureProfileHandle(id, mUserId);
+ return mService.getPictureProfileHandle(id, mUserHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets sound profile handle by profile ID.
+ * @hide
+ */
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] id) {
+ try {
+ return mService.getSoundProfileHandle(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -308,10 +337,12 @@ public final class MediaQualityManager {
*
* <p>If the profile is created successfully,
* {@link PictureProfileCallback#onPictureProfileAdded(String, PictureProfile)} is invoked.
+ *
+ * @param pp the {@link PictureProfile} object to be created.
*/
public void createPictureProfile(@NonNull PictureProfile pp) {
try {
- mService.createPictureProfile(pp, mUserId);
+ mService.createPictureProfile(pp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -320,10 +351,13 @@ public final class MediaQualityManager {
/**
* Updates an existing picture profile and store it in the system.
+ *
+ * @param profileId the id of the object to be updated.
+ * @param pp the {@link PictureProfile} object to be updated.
*/
public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
try {
- mService.updatePictureProfile(profileId, pp, mUserId);
+ mService.updatePictureProfile(profileId, pp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -332,10 +366,12 @@ public final class MediaQualityManager {
/**
* Removes a picture profile from the system.
+ *
+ * @param profileId the id of the object to be removed.
*/
public void removePictureProfile(@NonNull String profileId) {
try {
- mService.removePictureProfile(profileId, mUserId);
+ mService.removePictureProfile(profileId, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -371,18 +407,20 @@ public final class MediaQualityManager {
}
}
-
/**
* Gets sound profile by given profile type and name.
*
- * @return the corresponding sound profile if available; {@code null} if the name doesn't
- * exist.
+ * @param type the type of the profile.
+ * @param name the name of the profile.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ * @return the corresponding sound profile if available; {@code null} if the name doesn't exist.
*/
@Nullable
public SoundProfile getSoundProfile(
- @SoundProfile.ProfileType int type, @NonNull String name) {
+ @SoundProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
try {
- return mService.getSoundProfile(type, name, mUserId);
+ return mService.getSoundProfile(type, name, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -392,14 +430,18 @@ public final class MediaQualityManager {
/**
* Gets profiles that available to the given package.
*
+ * @param packageName the package name of the profiles.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
- public List<SoundProfile> getSoundProfilesByPackage(@NonNull String packageName) {
+ public List<SoundProfile> getSoundProfilesByPackage(
+ @NonNull String packageName, boolean includeParams) {
try {
- return mService.getSoundProfilesByPackage(packageName, mUserId);
+ return mService.getSoundProfilesByPackage(packageName, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,11 +449,16 @@ public final class MediaQualityManager {
/**
* Gets profiles that available to the caller package.
+ *
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ *
+ * @return the corresponding sound profile if available; {@code null} if the none available.
*/
@NonNull
- public List<SoundProfile> getAvailableSoundProfiles() {
+ public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams) {
try {
- return mService.getAvailableSoundProfiles(mUserId);
+ return mService.getAvailableSoundProfiles(includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -429,7 +476,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public boolean setDefaultSoundProfile(@Nullable String id) {
try {
- return mService.setDefaultSoundProfile(id, mUserId);
+ return mService.setDefaultSoundProfile(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -438,7 +485,7 @@ public final class MediaQualityManager {
/**
* Gets all package names whose sound profiles are available.
*
- * @see #getSoundProfilesByPackage(String)
+ * @see #getSoundProfilesByPackage(String, boolean)
*
* @hide
*/
@@ -447,7 +494,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<String> getSoundProfilePackageNames() {
try {
- return mService.getSoundProfilePackageNames(mUserId);
+ return mService.getSoundProfilePackageNames(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -459,10 +506,12 @@ public final class MediaQualityManager {
*
* <p>If the profile is created successfully,
* {@link SoundProfileCallback#onSoundProfileAdded(String, SoundProfile)} is invoked.
+ *
+ * @param sp the {@link SoundProfile} object to be created.
*/
public void createSoundProfile(@NonNull SoundProfile sp) {
try {
- mService.createSoundProfile(sp, mUserId);
+ mService.createSoundProfile(sp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -471,10 +520,13 @@ public final class MediaQualityManager {
/**
* Updates an existing sound profile and store it in the system.
+ *
+ * @param profileId the id of the object to be updated.
+ * @param sp the {@link SoundProfile} object to be updated.
*/
public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
try {
- mService.updateSoundProfile(profileId, sp, mUserId);
+ mService.updateSoundProfile(profileId, sp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -483,10 +535,12 @@ public final class MediaQualityManager {
/**
* Removes a sound profile from the system.
+ *
+ * @param profileId the id of the object to be removed.
*/
public void removeSoundProfile(@NonNull String profileId) {
try {
- mService.removeSoundProfile(profileId, mUserId);
+ mService.removeSoundProfile(profileId, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -498,7 +552,7 @@ public final class MediaQualityManager {
@NonNull
public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
try {
- return mService.getParamCapabilities(names, mUserId);
+ return mService.getParamCapabilities(names, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -516,7 +570,7 @@ public final class MediaQualityManager {
@NonNull
public List<String> getPictureProfileAllowList() {
try {
- return mService.getPictureProfileAllowList(mUserId);
+ return mService.getPictureProfileAllowList(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -530,7 +584,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setPictureProfileAllowList(packageNames, mUserId);
+ mService.setPictureProfileAllowList(packageNames, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -548,7 +602,7 @@ public final class MediaQualityManager {
@NonNull
public List<String> getSoundProfileAllowList() {
try {
- return mService.getSoundProfileAllowList(mUserId);
+ return mService.getSoundProfileAllowList(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -562,7 +616,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setSoundProfileAllowList(packageNames, mUserId);
+ mService.setSoundProfileAllowList(packageNames, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -574,7 +628,7 @@ public final class MediaQualityManager {
*/
public boolean isSupported() {
try {
- return mService.isSupported(mUserId);
+ return mService.isSupported(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -592,7 +646,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setAutoPictureQualityEnabled(boolean enabled) {
try {
- mService.setAutoPictureQualityEnabled(enabled, mUserId);
+ mService.setAutoPictureQualityEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -603,7 +657,7 @@ public final class MediaQualityManager {
*/
public boolean isAutoPictureQualityEnabled() {
try {
- return mService.isAutoPictureQualityEnabled(mUserId);
+ return mService.isAutoPictureQualityEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -620,7 +674,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setSuperResolutionEnabled(boolean enabled) {
try {
- mService.setSuperResolutionEnabled(enabled, mUserId);
+ mService.setSuperResolutionEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -631,7 +685,7 @@ public final class MediaQualityManager {
*/
public boolean isSuperResolutionEnabled() {
try {
- return mService.isSuperResolutionEnabled(mUserId);
+ return mService.isSuperResolutionEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -649,7 +703,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setAutoSoundQualityEnabled(boolean enabled) {
try {
- mService.setAutoSoundQualityEnabled(enabled, mUserId);
+ mService.setAutoSoundQualityEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -660,7 +714,7 @@ public final class MediaQualityManager {
*/
public boolean isAutoSoundQualityEnabled() {
try {
- return mService.isAutoSoundQualityEnabled(mUserId);
+ return mService.isAutoSoundQualityEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -709,7 +763,7 @@ public final class MediaQualityManager {
@NonNull AmbientBacklightSettings settings) {
Preconditions.checkNotNull(settings);
try {
- mService.setAmbientBacklightSettings(settings, mUserId);
+ mService.setAmbientBacklightSettings(settings, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -720,7 +774,7 @@ public final class MediaQualityManager {
*/
public boolean isAmbientBacklightEnabled() {
try {
- return mService.isAmbientBacklightEnabled(mUserId);
+ return mService.isAmbientBacklightEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -734,7 +788,7 @@ public final class MediaQualityManager {
@RequiresPermission(android.Manifest.permission.READ_COLOR_ZONES)
public void setAmbientBacklightEnabled(boolean enabled) {
try {
- mService.setAmbientBacklightEnabled(enabled, mUserId);
+ mService.setAmbientBacklightEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -791,11 +845,11 @@ public final class MediaQualityManager {
});
}
- public void postError(int error) {
+ public void postError(String profileId, int error) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- mCallback.onError(error);
+ mCallback.onError(profileId, error);
}
});
}
@@ -851,11 +905,11 @@ public final class MediaQualityManager {
});
}
- public void postError(int error) {
+ public void postError(String profileId, int error) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- mCallback.onError(error);
+ mCallback.onError(profileId, error);
}
});
}
@@ -921,9 +975,11 @@ public final class MediaQualityManager {
/**
* This is invoked when an issue has occurred.
*
+ * @param profileId the profile ID related to the error. {@code null} if there is no
+ * associated profile.
* @param errorCode the error code
*/
- public void onError(@PictureProfile.ErrorCode int errorCode) {
+ public void onError(@Nullable String profileId, @PictureProfile.ErrorCode int errorCode) {
}
/**
@@ -976,9 +1032,11 @@ public final class MediaQualityManager {
/**
* This is invoked when an issue has occurred.
*
+ * @param profileId the profile ID related to the error. {@code null} if there is no
+ * associated profile.
* @param errorCode the error code
*/
- public void onError(@SoundProfile.ErrorCode int errorCode) {
+ public void onError(@Nullable String profileId, @SoundProfile.ErrorCode int errorCode) {
}
/**
@@ -1004,4 +1062,86 @@ public final class MediaQualityManager {
public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) {
}
}
+
+ /**
+ * Listener used to monitor status of active pictures.
+ */
+ public interface ActiveProcessingPictureListener {
+ /**
+ * Called when active pictures are changed.
+ *
+ * @param activeProcessingPictures contents currently undergoing picture processing.
+ */
+ void onActiveProcessingPicturesChanged(
+ @NonNull List<ActiveProcessingPicture> activeProcessingPictures);
+ }
+
+ /**
+ * Adds an active picture listener for the contents owner by the caller.
+ */
+ public void addActiveProcessingPictureListener(
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mApListenerRecords.add(
+ new ActiveProcessingPictureListenerRecord(listener, executor, false));
+ }
+ }
+
+ /**
+ * Adds an active picture listener for all contents.
+ *
+ * @hide
+ */
+ @SuppressLint("PairedRegistration")
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ public void addGlobalActiveProcessingPictureListener(
+ @NonNull Executor executor,
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mApListenerRecords.add(
+ new ActiveProcessingPictureListenerRecord(listener, executor, true));
+ }
+ }
+
+
+ /**
+ * Removes an active picture listener for the contents.
+ */
+ public void removeActiveProcessingPictureListener(
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ synchronized (mLock) {
+ for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
+ it.hasNext(); ) {
+ ActiveProcessingPictureListenerRecord record = it.next();
+ if (record.getListener() == listener) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ private static final class ActiveProcessingPictureListenerRecord {
+ private final ActiveProcessingPictureListener mListener;
+ private final Executor mExecutor;
+ private final boolean mIsGlobal;
+
+ ActiveProcessingPictureListenerRecord(
+ ActiveProcessingPictureListener listener, Executor executor, boolean isGlobal) {
+ mListener = listener;
+ mExecutor = executor;
+ mIsGlobal = isGlobal;
+ }
+
+ public ActiveProcessingPictureListener getListener() {
+ return mListener;
+ }
+ }
}
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index dcb4222c3eaf..6064485c1c38 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -48,6 +48,7 @@ public final class PictureProfile implements Parcelable {
private final String mPackageName;
@NonNull
private final PersistableBundle mParams;
+ private final PictureProfileHandle mHandle;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -121,6 +122,7 @@ public final class PictureProfile implements Parcelable {
mInputId = in.readString();
mPackageName = in.readString();
mParams = in.readPersistableBundle();
+ mHandle = in.readParcelable(PictureProfileHandle.class.getClassLoader());
}
@Override
@@ -131,6 +133,7 @@ public final class PictureProfile implements Parcelable {
dest.writeString(mInputId);
dest.writeString(mPackageName);
dest.writePersistableBundle(mParams);
+ dest.writeParcelable(mHandle, flags);
}
@Override
@@ -163,13 +166,15 @@ public final class PictureProfile implements Parcelable {
@NonNull String name,
@Nullable String inputId,
@NonNull String packageName,
- @NonNull PersistableBundle params) {
+ @NonNull PersistableBundle params,
+ @NonNull PictureProfileHandle handle) {
this.mId = id;
this.mType = type;
this.mName = name;
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
+ this.mHandle = handle;
}
/**
@@ -251,6 +256,15 @@ public final class PictureProfile implements Parcelable {
}
/**
+ * Gets profile handle
+ * @hide
+ */
+ @NonNull
+ public PictureProfileHandle getHandle() {
+ return mHandle;
+ }
+
+ /**
* A builder for {@link PictureProfile}.
*/
public static final class Builder {
@@ -265,6 +279,7 @@ public final class PictureProfile implements Parcelable {
private String mPackageName;
@NonNull
private PersistableBundle mParams;
+ private PictureProfileHandle mHandle;
/**
* Creates a new Builder.
@@ -283,6 +298,7 @@ public final class PictureProfile implements Parcelable {
mPackageName = p.getPackageName();
mInputId = p.getInputId();
mParams = p.getParameters();
+ mHandle = p.getHandle();
}
/**
@@ -350,6 +366,16 @@ public final class PictureProfile implements Parcelable {
}
/**
+ * Sets profile handle.
+ * @hide
+ */
+ @NonNull
+ public Builder setHandle(@NonNull PictureProfileHandle handle) {
+ mHandle = handle;
+ return this;
+ }
+
+ /**
* Builds the instance.
*/
@NonNull
@@ -361,7 +387,8 @@ public final class PictureProfile implements Parcelable {
mName,
mInputId,
mPackageName,
- mParams);
+ mParams,
+ mHandle);
return o;
}
}
diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java
index 714fd36d664a..d9d21932d09a 100644
--- a/media/java/android/media/quality/PictureProfileHandle.java
+++ b/media/java/android/media/quality/PictureProfileHandle.java
@@ -28,11 +28,14 @@ import android.os.Parcelable;
* A picture profile represents a collection of parameters used to configure picture processing
* to enhance the quality of graphic buffers.
*
+ * @see PictureProfile.getHandle
+ *
* @hide
*/
@SystemApi
@FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES)
public final class PictureProfileHandle implements Parcelable {
+ /** A handle that represents no picture processing configuration. */
public static final @NonNull PictureProfileHandle NONE = new PictureProfileHandle(0);
private final long mId;
@@ -42,7 +45,16 @@ public final class PictureProfileHandle implements Parcelable {
mId = id;
}
- /** @hide */
+ /**
+ * An ID that uniquely identifies the picture profile across the system.
+ *
+ * This ID can be used to construct an NDK PictureProfileHandle to be fed directly into
+ * IGraphicBufferProducer to couple a picture profile to a graphic buffer.
+ *
+ * Note: These IDs are generated randomly and are not stable across reboots.
+ *
+ * @hide
+ */
@SystemApi
@FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES)
public long getId() {
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index c7fb4dd8486f..1dd59ab0903b 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -48,6 +48,7 @@ public final class SoundProfile implements Parcelable {
private final String mPackageName;
@NonNull
private final PersistableBundle mParams;
+ private final SoundProfileHandle mHandle;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -120,6 +121,7 @@ public final class SoundProfile implements Parcelable {
mInputId = in.readString();
mPackageName = in.readString();
mParams = in.readPersistableBundle();
+ mHandle = in.readParcelable(SoundProfileHandle.class.getClassLoader());
}
@Override
@@ -130,6 +132,7 @@ public final class SoundProfile implements Parcelable {
dest.writeString(mInputId);
dest.writeString(mPackageName);
dest.writePersistableBundle(mParams);
+ dest.writeParcelable(mHandle, flags);
}
@Override
@@ -162,13 +165,15 @@ public final class SoundProfile implements Parcelable {
@NonNull String name,
@Nullable String inputId,
@NonNull String packageName,
- @NonNull PersistableBundle params) {
+ @NonNull PersistableBundle params,
+ @NonNull SoundProfileHandle handle) {
this.mId = id;
this.mType = type;
this.mName = name;
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
+ this.mHandle = handle;
}
/**
@@ -250,6 +255,15 @@ public final class SoundProfile implements Parcelable {
}
/**
+ * Gets profile handle
+ * @hide
+ */
+ @NonNull
+ public SoundProfileHandle getHandle() {
+ return mHandle;
+ }
+
+ /**
* A builder for {@link SoundProfile}
*/
public static final class Builder {
@@ -264,6 +278,7 @@ public final class SoundProfile implements Parcelable {
private String mPackageName;
@NonNull
private PersistableBundle mParams;
+ private SoundProfileHandle mHandle;
/**
* Creates a new Builder.
@@ -282,6 +297,7 @@ public final class SoundProfile implements Parcelable {
mPackageName = p.getPackageName();
mInputId = p.getInputId();
mParams = p.getParameters();
+ mHandle = p.getHandle();
}
/**
@@ -349,6 +365,16 @@ public final class SoundProfile implements Parcelable {
}
/**
+ * Sets profile handle.
+ * @hide
+ */
+ @NonNull
+ public Builder setHandle(@NonNull SoundProfileHandle handle) {
+ mHandle = handle;
+ return this;
+ }
+
+ /**
* Builds the instance.
*/
@NonNull
@@ -360,7 +386,8 @@ public final class SoundProfile implements Parcelable {
mName,
mInputId,
mPackageName,
- mParams);
+ mParams,
+ mHandle);
return o;
}
}
diff --git a/media/java/android/media/quality/SoundProfileHandle.aidl b/media/java/android/media/quality/SoundProfileHandle.aidl
new file mode 100644
index 000000000000..6b8161c8cc43
--- /dev/null
+++ b/media/java/android/media/quality/SoundProfileHandle.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.quality;
+
+parcelable SoundProfileHandle;
diff --git a/media/java/android/media/quality/SoundProfileHandle.java b/media/java/android/media/quality/SoundProfileHandle.java
new file mode 100644
index 000000000000..edb546efdaf3
--- /dev/null
+++ b/media/java/android/media/quality/SoundProfileHandle.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.media.quality;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A type-safe handle to a sound profile.
+ *
+ * @hide
+ */
+public final class SoundProfileHandle implements Parcelable {
+ public static final @NonNull SoundProfileHandle NONE = new SoundProfileHandle(-1000);
+
+ private final long mId;
+
+ /** @hide */
+ public SoundProfileHandle(long id) {
+ mId = id;
+ }
+
+ /** @hide */
+ public long getId() {
+ return mId;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mId);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ public static final @NonNull Creator<SoundProfileHandle> CREATOR =
+ new Creator<SoundProfileHandle>() {
+ @Override
+ public SoundProfileHandle createFromParcel(Parcel in) {
+ return new SoundProfileHandle(in);
+ }
+
+ @Override
+ public SoundProfileHandle[] newArray(int size) {
+ return new SoundProfileHandle[size];
+ }
+ };
+
+ private SoundProfileHandle(@NonNull Parcel in) {
+ mId = in.readLong();
+ }
+}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index fc184fe5c872..8419ce761a4a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1149,9 +1149,9 @@ status_t JMediaCodec::unsubscribeFromVendorParameters(JNIEnv *env, jobject names
static jobject getJavaResources(
JNIEnv *env,
- const std::vector<MediaCodec::InstanceResourceInfo>& resources) {
+ const std::vector<InstanceResourceInfo>& resources) {
jobject resourcesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
- for (const MediaCodec::InstanceResourceInfo& res : resources) {
+ for (const InstanceResourceInfo& res : resources) {
ScopedLocalRef<jobject> object{env, env->NewObject(
gInstanceResourceInfo.clazz, gInstanceResourceInfo.ctorId)};
ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(res.mName.c_str())};
@@ -1169,7 +1169,7 @@ static jobject getJavaResources(
}
status_t JMediaCodec::getRequiredResources(JNIEnv *env, jobject *resourcesObj) {
- std::vector<MediaCodec::InstanceResourceInfo> resources;
+ std::vector<InstanceResourceInfo> resources;
status_t status = mCodec->getRequiredResources(resources);
if (status != OK) {
return status;
@@ -3615,9 +3615,9 @@ static void android_media_MediaCodec_unsubscribeFromVendorParameters(
static jobject getJavaResources(
JNIEnv *env,
- const std::vector<MediaCodec::GlobalResourceInfo>& resources) {
+ const std::vector<GlobalResourceInfo>& resources) {
jobject resourcesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
- for (const MediaCodec::GlobalResourceInfo& res : resources) {
+ for (const GlobalResourceInfo& res : resources) {
ScopedLocalRef<jobject> object{env, env->NewObject(
gGlobalResourceInfo.clazz, gGlobalResourceInfo.ctorId)};
ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(res.mName.c_str())};
@@ -3633,7 +3633,7 @@ static jobject getJavaResources(
static jobject android_media_MediaCodec_getGloballyAvailableResources(
JNIEnv *env, jobject thiz) {
(void)thiz;
- std::vector<MediaCodec::GlobalResourceInfo> resources;
+ std::vector<GlobalResourceInfo> resources;
status_t status = MediaCodec::getGloballyAvailableResources(resources);
if (status != OK) {
if (status == ERROR_UNSUPPORTED) {
diff --git a/native/android/Android.bp b/native/android/Android.bp
index cd6de5a5c8f0..129d6163010e 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -73,6 +73,7 @@ cc_library_shared {
"surface_control.cpp",
"surface_texture.cpp",
"system_fonts.cpp",
+ "system_health.cpp",
"trace.cpp",
"thermal.cpp",
],
diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp
index 532213611cf1..ee2cd6f3fcde 100644
--- a/native/android/dynamic_instrumentation_manager.cpp
+++ b/native/android/dynamic_instrumentation_manager.cpp
@@ -15,7 +15,9 @@
*/
#define LOG_TAG "ADynamicInstrumentationManager"
+#include <android-base/properties.h>
#include <android/dynamic_instrumentation_manager.h>
+#include <android/os/instrumentation/BnOffsetCallback.h>
#include <android/os/instrumentation/ExecutableMethodFileOffsets.h>
#include <android/os/instrumentation/IDynamicInstrumentationManager.h>
#include <android/os/instrumentation/MethodDescriptor.h>
@@ -23,7 +25,9 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <future>
#include <mutex>
#include <optional>
#include <string>
@@ -31,6 +35,9 @@
namespace android::dynamicinstrumentationmanager {
+using android::os::instrumentation::BnOffsetCallback;
+using android::os::instrumentation::ExecutableMethodFileOffsets;
+
// Global instance of IDynamicInstrumentationManager, service is obtained only on first use.
static std::mutex mLock;
static sp<os::instrumentation::IDynamicInstrumentationManager> mService;
@@ -66,6 +73,7 @@ ADynamicInstrumentationManager_TargetProcess* ADynamicInstrumentationManager_Tar
void ADynamicInstrumentationManager_TargetProcess_destroy(
const ADynamicInstrumentationManager_TargetProcess* instance) {
+ if (instance == nullptr) return;
delete instance;
}
@@ -97,6 +105,7 @@ ADynamicInstrumentationManager_MethodDescriptor_create(const char* fullyQualifie
void ADynamicInstrumentationManager_MethodDescriptor_destroy(
const ADynamicInstrumentationManager_MethodDescriptor* instance) {
+ if (instance == nullptr) return;
delete instance;
}
@@ -128,9 +137,34 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOff
void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ if (instance == nullptr) return;
delete instance;
}
+class ResultCallback : public BnOffsetCallback {
+public:
+ ::android::binder::Status onResult(
+ const ::std::optional<ExecutableMethodFileOffsets>& offsets) override {
+ promise_.set_value(offsets);
+ return android::binder::Status::ok();
+ }
+
+ std::optional<ExecutableMethodFileOffsets> waitForResult() {
+ std::future<std::optional<ExecutableMethodFileOffsets>> futureResult =
+ promise_.get_future();
+ auto futureStatus = futureResult.wait_for(
+ std::chrono::seconds(1 * android::base::HwTimeoutMultiplier()));
+ if (futureStatus == std::future_status::ready) {
+ return futureResult.get();
+ } else {
+ return std::nullopt;
+ }
+ }
+
+private:
+ std::promise<std::optional<ExecutableMethodFileOffsets>> promise_;
+};
+
int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
const ADynamicInstrumentationManager_TargetProcess* targetProcess,
const ADynamicInstrumentationManager_MethodDescriptor* methodDescriptor,
@@ -150,15 +184,15 @@ int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
return INVALID_OPERATION;
}
- std::optional<android::os::instrumentation::ExecutableMethodFileOffsets> offsets;
+ android::sp<ResultCallback> resultCallback = android::sp<ResultCallback>::make();
binder_status_t result =
service->getExecutableMethodFileOffsets(targetProcessParcel, methodDescriptorParcel,
- &offsets)
+ resultCallback)
.exceptionCode();
if (result != OK) {
return result;
}
-
+ std::optional<ExecutableMethodFileOffsets> offsets = resultCallback->waitForResult();
if (offsets != std::nullopt) {
auto* value = new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
value->containerPath = offsets->containerPath;
@@ -170,4 +204,4 @@ int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
}
return result;
-} \ No newline at end of file
+}
diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h
index ab9f37034a22..7bb7615bc3a1 100644
--- a/native/android/include_platform/android/dynamic_instrumentation_manager.h
+++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h
@@ -40,9 +40,12 @@ typedef struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets
*
* @param uid of targeted process.
* @param pid of targeted process.
- * @param processName to disambiguate from corner cases that may arise from pid reuse.
+ * @param processName UTF-8 encoded string representing the same process as specified by `pid`.
+ * Supplied to disambiguate from corner cases that may arise from pid reuse.
+ * Referenced parameter must outlive the returned
+ * ADynamicInstrumentationManager_TargetProcess.
*/
-ADynamicInstrumentationManager_TargetProcess* _Nonnull
+ADynamicInstrumentationManager_TargetProcess* _Nullable
ADynamicInstrumentationManager_TargetProcess_create(
uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36);
/**
@@ -51,22 +54,27 @@ ADynamicInstrumentationManager_TargetProcess* _Nonnull
* @param instance returned from ADynamicInstrumentationManager_TargetProcess_create.
*/
void ADynamicInstrumentationManager_TargetProcess_destroy(
- const ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36);
+ const ADynamicInstrumentationManager_TargetProcess* _Nullable instance) __INTRODUCED_IN(36);
/**
* Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they
- * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy.
+ * are done with ADynamicInstrumentationManager_MethodDescriptor_destroy.
*
- * @param fullyQualifiedClassName fqcn of class containing the method.
- * @param methodName
- * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for
- * primitives.
+ * @param fullyQualifiedClassName UTF-8 encoded fqcn of class containing the method. Referenced
+ * parameter must outlive the returned
+ * ADynamicInstrumentationManager_MethodDescriptor.
+ * @param methodName UTF-8 encoded method name. Referenced parameter must outlive the returned
+ * ADynamicInstrumentationManager_MethodDescriptor.
+ * @param fullyQualifiedParameters UTF-8 encoded fqcn of parameters of the method's signature,
+ * or e.g. "int" for primitives. Referenced parameter should
+ * outlive the returned
+ * ADynamicInstrumentationManager_MethodDescriptor.
* @param numParameters length of `fullyQualifiedParameters` array.
*/
-ADynamicInstrumentationManager_MethodDescriptor* _Nonnull
+ADynamicInstrumentationManager_MethodDescriptor* _Nullable
ADynamicInstrumentationManager_MethodDescriptor_create(
const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName,
- const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters)
+ const char* _Nonnull* _Nonnull fullyQualifiedParameters, size_t numParameters)
__INTRODUCED_IN(36);
/**
* Clean up an ADynamicInstrumentationManager_MethodDescriptor.
@@ -74,14 +82,16 @@ ADynamicInstrumentationManager_MethodDescriptor* _Nonnull
* @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create.
*/
void ADynamicInstrumentationManager_MethodDescriptor_destroy(
- const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance)
+ const ADynamicInstrumentationManager_MethodDescriptor* _Nullable instance)
__INTRODUCED_IN(36);
/**
* Get the containerPath calculated by
* ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
* @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The OS path of the containing file.
+ * @return The OS path of the containing file as a UTF-8 string, which has the same lifetime
+ * as the ADynamicInstrumentationManager_ExecutableMethodFileOffsets instance passed
+ * as a param.
*/
const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -90,7 +100,8 @@ const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets
* Get the containerOffset calculated by
* ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
* @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The offset of the containing file within the process' memory.
+ * @return The absolute address of the containing file within remote the process' virtual memory
+ * space.
*/
uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -98,7 +109,8 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainer
/**
* Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
* @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The offset of the method within the containing file.
+ * @return The offset of the method within the container whose address is returned by
+ * ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset.
*/
uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -109,7 +121,7 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOff
* @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
*/
void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
- const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nullable instance)
__INTRODUCED_IN(36);
/**
* Provides ART metadata about the described java method within the target process.
@@ -118,7 +130,9 @@ void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
* @param methodDescriptor describes the targeted method.
* @param out will be populated with the data if successful. A nullptr combined
* with an OK status means that the program method is defined, but the offset
- * info was unavailable because it is not AOT compiled.
+ * info was unavailable because it is not AOT compiled. Caller owns `out` and
+ * should clean it up with
+ * ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy.
* @return status indicating success or failure. The values correspond to the `binder_exception_t`
* enum values from <android/binder_status.h>.
*/
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 78a7ddb5b0cc..1ccadf90c2a9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -320,6 +320,23 @@ LIBANDROID {
ASystemFontIterator_open; # introduced=29
ASystemFontIterator_close; # introduced=29
ASystemFontIterator_next; # introduced=29
+ ASystemHealth_getCpuHeadroom; # introduced=36
+ ASystemHealth_getGpuHeadroom; # introduced=36
+ ASystemHealth_getCpuHeadroomMinIntervalMillis; # introduced=36
+ ASystemHealth_getGpuHeadroomMinIntervalMillis; # introduced=36
+ ACpuHeadroomParams_create; # introduced=36
+ ACpuHeadroomParams_destroy; # introduced=36
+ ACpuHeadroomParams_setCalculationType; # introduced=36
+ ACpuHeadroomParams_getCalculationType; # introduced=36
+ ACpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_getCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_setTids; # introduced=36
+ AGpuHeadroomParams_create; # introduced=36
+ AGpuHeadroomParams_destroy; # introduced=36
+ AGpuHeadroomParams_setCalculationType; # introduced=36
+ AGpuHeadroomParams_getCalculationType; # introduced=36
+ AGpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ AGpuHeadroomParams_getCalculationWindowMillis; # introduced=36
AFont_close; # introduced=29
AFont_getFontFilePath; # introduced=29
AFont_getWeight; # introduced=29
@@ -376,6 +393,7 @@ LIBANDROID {
APerformanceHint_createSessionUsingConfig; # introduced=36
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_notifyWorkloadSpike; # introduced=36
APerformanceHint_borrowSessionFromJava; # introduced=36
APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 608c01caee96..9257901bcd1f 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -22,6 +22,7 @@
#include <aidl/android/hardware/power/SessionHint.h>
#include <aidl/android/hardware/power/SessionMode.h>
#include <aidl/android/hardware/power/SessionTag.h>
+#include <aidl/android/hardware/power/SupportInfo.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <aidl/android/hardware/power/WorkDurationFixedV1.h>
#include <aidl/android/os/IHintManager.h>
@@ -148,10 +149,36 @@ private:
std::future<bool> mChannelCreationFinished;
};
+class SupportInfoWrapper {
+public:
+ SupportInfoWrapper(hal::SupportInfo& info);
+ bool isSessionModeSupported(hal::SessionMode mode);
+ bool isSessionHintSupported(hal::SessionHint hint);
+
+private:
+ template <class T>
+ bool getEnumSupportFromBitfield(T& enumValue, int64_t& supportBitfield) {
+ // extract the bit corresponding to the enum by shifting the bitfield
+ // over that much and cutting off any extra values
+ return (supportBitfield >> static_cast<int>(enumValue)) % 2;
+ }
+ hal::SupportInfo mSupportInfo;
+};
+
+class HintManagerClient : public IHintManager::BnHintManagerClient {
+public:
+ // Currently a no-op that exists for FMQ init to call in the future
+ ndk::ScopedAStatus receiveChannelConfig(const hal::ChannelConfig&) {
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
struct APerformanceHintManager {
public:
static APerformanceHintManager* getInstance();
- APerformanceHintManager(std::shared_ptr<IHintManager>& service, int64_t preferredRateNanos);
+ APerformanceHintManager(std::shared_ptr<IHintManager>& service,
+ IHintManager::HintManagerClientData&& clientData,
+ std::shared_ptr<HintManagerClient> callbackClient);
APerformanceHintManager() = delete;
~APerformanceHintManager();
@@ -169,29 +196,21 @@ public:
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
void initJava(JNIEnv* _Nonnull env);
- ndk::ScopedAIBinder_Weak x;
template <class T>
static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
ASurfaceControl** controls, int numSurfaceControls,
std::vector<T>& out);
+ ndk::SpAIBinder& getToken();
+ SupportInfoWrapper& getSupportInfo();
private:
- // Necessary to create an empty binder object
- static void* tokenStubOnCreate(void*) {
- return nullptr;
- }
- static void tokenStubOnDestroy(void*) {}
- static binder_status_t tokenStubOnTransact(AIBinder*, transaction_code_t, const AParcel*,
- AParcel*) {
- return STATUS_OK;
- }
-
static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager);
std::shared_ptr<IHintManager> mHintManager;
+ std::shared_ptr<HintManagerClient> mCallbackClient;
+ IHintManager::HintManagerClientData mClientData;
+ SupportInfoWrapper mSupportInfoWrapper;
ndk::SpAIBinder mToken;
- const int64_t mPreferredRateNanos;
- std::optional<int32_t> mMaxGraphicsPipelineThreadsCount;
FMQWrapper mFMQWrapper;
double mHintBudget = kMaxLoadHintsPerInterval;
int64_t mLastBudgetReplenish = 0;
@@ -214,6 +233,7 @@ public:
int sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char* debugName);
int notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName);
int notifyWorkloadReset(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
@@ -272,14 +292,27 @@ static FMQWrapper& getFMQ() {
return APerformanceHintManager::getInstance()->getFMQWrapper();
}
+// ===================================== SupportInfoWrapper implementation
+
+SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {}
+
+bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) {
+ return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints);
+}
+
+bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) {
+ return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes);
+}
+
// ===================================== APerformanceHintManager implementation
APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager>& manager,
- int64_t preferredRateNanos)
- : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {
- static AIBinder_Class* tokenBinderClass =
- AIBinder_Class_define("phm_token", tokenStubOnCreate, tokenStubOnDestroy,
- tokenStubOnTransact);
- mToken = ndk::SpAIBinder(AIBinder_new(tokenBinderClass, nullptr));
+ IHintManager::HintManagerClientData&& clientData,
+ std::shared_ptr<HintManagerClient> callbackClient)
+ : mHintManager(std::move(manager)),
+ mCallbackClient(callbackClient),
+ mClientData(clientData),
+ mSupportInfoWrapper(clientData.supportInfo),
+ mToken(callbackClient->asBinder()) {
if (mFMQWrapper.isSupported()) {
mFMQWrapper.setToken(mToken);
mFMQWrapper.startChannel(mHintManager.get());
@@ -314,21 +347,22 @@ APerformanceHintManager* APerformanceHintManager::create(std::shared_ptr<IHintMa
ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
return nullptr;
}
- int64_t preferredRateNanos = -1L;
- ndk::ScopedAStatus ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
+ std::shared_ptr<HintManagerClient> client = ndk::SharedRefBase::make<HintManagerClient>();
+ IHintManager::HintManagerClientData clientData;
+ ndk::ScopedAStatus ret = manager->registerClient(client, &clientData);
if (!ret.isOk()) {
- ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, ret.getMessage());
+ ALOGE("%s: PerformanceHint is not supported. %s", __FUNCTION__, ret.getMessage());
return nullptr;
}
- if (preferredRateNanos <= 0) {
- preferredRateNanos = -1L;
+ if (clientData.preferredRateNanos <= 0) {
+ clientData.preferredRateNanos = -1L;
}
- return new APerformanceHintManager(manager, preferredRateNanos);
+ return new APerformanceHintManager(manager, std::move(clientData), client);
}
bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
mHintBudget =
- std::max(kMaxLoadHintsPerInterval,
+ std::min(kMaxLoadHintsPerInterval,
mHintBudget +
static_cast<double>(now - mLastBudgetReplenish) * kReplenishRate);
mLastBudgetReplenish = now;
@@ -388,7 +422,9 @@ APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
return nullptr;
}
- auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
+
+ auto out = new APerformanceHintSession(mHintManager, std::move(session),
+ mClientData.preferredRateNanos,
sessionCreationConfig->targetWorkDurationNanos, isJava,
sessionConfig.id == -1
? std::nullopt
@@ -415,24 +451,11 @@ APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env
}
int64_t APerformanceHintManager::getPreferredRateNanos() const {
- return mPreferredRateNanos;
+ return mClientData.preferredRateNanos;
}
int32_t APerformanceHintManager::getMaxGraphicsPipelineThreadsCount() {
- if (!mMaxGraphicsPipelineThreadsCount.has_value()) {
- int32_t threadsCount = -1;
- ndk::ScopedAStatus ret = mHintManager->getMaxGraphicsPipelineThreadsCount(&threadsCount);
- if (!ret.isOk()) {
- ALOGE("%s: PerformanceHint cannot get max graphics pipeline threads count. %s",
- __FUNCTION__, ret.getMessage());
- return -1;
- }
- if (threadsCount <= 0) {
- threadsCount = -1;
- }
- mMaxGraphicsPipelineThreadsCount.emplace(threadsCount);
- }
- return mMaxGraphicsPipelineThreadsCount.value();
+ return mClientData.maxGraphicsPipelineThreads;
}
FMQWrapper& APerformanceHintManager::getFMQWrapper() {
@@ -449,6 +472,14 @@ void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) {
mJavaInitialized = true;
}
+ndk::SpAIBinder& APerformanceHintManager::getToken() {
+ return mToken;
+}
+
+SupportInfoWrapper& APerformanceHintManager::getSupportInfo() {
+ return mSupportInfoWrapper;
+}
+
// ===================================== APerformanceHintSession implementation
constexpr int kNumEnums = enum_size<hal::SessionHint>();
@@ -600,6 +631,19 @@ int APerformanceHintSession::notifyWorkloadReset(bool cpu, bool gpu, const char*
return sendHints(hints, now, debugName);
}
+int APerformanceHintSession::notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_SPIKE);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_SPIKE);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
if (size == 0) {
ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
@@ -1149,6 +1193,16 @@ int APerformanceHint_notifyWorkloadReset(APerformanceHintSession* session, bool
return session->notifyWorkloadReset(cpu, gpu, debugName);
}
+int APerformanceHint_notifyWorkloadSpike(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadSpike(cpu, gpu, debugName);
+}
+
int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
ANativeWindow** nativeWindows, int nativeWindowsSize,
ASurfaceControl** surfaceControls, int surfaceControlsSize) {
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
new file mode 100644
index 000000000000..f3fa9f6836d5
--- /dev/null
+++ b/native/android/system_health.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#include <aidl/android/hardware/power/CpuHeadroomParams.h>
+#include <aidl/android/hardware/power/GpuHeadroomParams.h>
+#include <aidl/android/os/CpuHeadroomParamsInternal.h>
+#include <aidl/android/os/GpuHeadroomParamsInternal.h>
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/system_health.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+
+using namespace android;
+using namespace aidl::android::os;
+namespace hal = aidl::android::hardware::power;
+
+struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {};
+struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {};
+
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int CPU_HEADROOM_MAX_TID_COUNT = 5;
+
+struct ASystemHealthManager {
+public:
+ static ASystemHealthManager* getInstance();
+ ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager);
+ ASystemHealthManager() = delete;
+ ~ASystemHealthManager();
+ int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom);
+ int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom);
+ int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+ int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+
+private:
+ static ASystemHealthManager* create(std::shared_ptr<IHintManager> hintManager);
+ std::shared_ptr<IHintManager> mHintManager;
+};
+
+ASystemHealthManager* ASystemHealthManager::getInstance() {
+ static std::once_flag creationFlag;
+ static ASystemHealthManager* instance = nullptr;
+ std::call_once(creationFlag, []() { instance = create(nullptr); });
+ return instance;
+}
+
+ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager)
+ : mHintManager(std::move(hintManager)) {}
+
+ASystemHealthManager::~ASystemHealthManager() {}
+
+ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> hintManager) {
+ if (!hintManager) {
+ hintManager = IHintManager::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService("performance_hint")));
+ }
+ if (hintManager == nullptr) {
+ ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
+ return nullptr;
+ }
+ return new ASystemHealthManager(hintManager);
+}
+
+ASystemHealthManager* ASystemHealth_acquireManager() {
+ return ASystemHealthManager::getInstance();
+}
+
+int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::CpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ CpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getCpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getCpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid ACpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getCpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ } else if (ret.getExceptionCode() == EX_SECURITY) {
+ return EPERM;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::GpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ GpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getGpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getGpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid AGpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getGpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
+ int tidsSize) {
+ LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
+ LOG_ALWAYS_FATAL_IF(tidsSize > CPU_HEADROOM_MAX_TID_COUNT, "%s: tids size should not exceed 5",
+ __FUNCTION__);
+ params->tids.resize(tidsSize);
+ params->tids.clear();
+ for (int i = 0; i < tidsSize; ++i) {
+ LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
+ tids[i]);
+ params->tids[i] = tids[i];
+ }
+}
+
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
+ ACpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of ACpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::CpuHeadroomParams::CalculationType>(calculationType);
+}
+
+ACpuHeadroomCalculationType ACpuHeadroomParams_getCalculationType(
+ ACpuHeadroomParams* _Nonnull params) {
+ return static_cast<ACpuHeadroomCalculationType>(params->calculationType);
+}
+
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
+ AGpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of AGpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::GpuHeadroomParams::CalculationType>(calculationType);
+}
+
+AGpuHeadroomCalculationType AGpuHeadroomParams_getCalculationType(
+ AGpuHeadroomParams* _Nonnull params) {
+ return static_cast<AGpuHeadroomCalculationType>(params->calculationType);
+}
+
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create() {
+ return new ACpuHeadroomParams();
+}
+
+AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create() {
+ return new AGpuHeadroomParams();
+}
+
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
+
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b8f574f44338..e3c10f63abb4 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -56,9 +56,6 @@ public:
const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
std::shared_ptr<IHintSession>* _aidl_return),
(override));
- MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
- MOCK_METHOD(ScopedAStatus, getMaxGraphicsPipelineThreadsCount, (int32_t* _aidl_return),
- (override));
MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
(const std::shared_ptr<IHintSession>& hintSession,
const ::std::vector<int32_t>& tids),
@@ -84,6 +81,11 @@ public:
MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+ MOCK_METHOD(ScopedAStatus, registerClient,
+ (const std::shared_ptr<::aidl::android::os::IHintManager::IHintManagerClient>&
+ clientDataIn,
+ ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+ (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -125,10 +127,9 @@ public:
APerformanceHintManager* createManager() {
APerformanceHint_setUseFMQForTesting(mUsingFMQ);
- ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
- .WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); }));
- ON_CALL(*mMockIHintManager, getMaxGraphicsPipelineThreadsCount(_))
- .WillByDefault(DoAll(SetArgPointee<0>(5), [] { return ScopedAStatus::ok(); }));
+ ON_CALL(*mMockIHintManager, registerClient(_, _))
+ .WillByDefault(
+ DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); }));
return APerformanceHint_getManager();
}
@@ -238,6 +239,20 @@ public:
int kMockQueueSize = 20;
bool mUsingFMQ = false;
+ IHintManager::HintManagerClientData mClientData{
+ .powerHalVersion = 6,
+ .maxGraphicsPipelineThreads = 5,
+ .preferredRateNanos = 123L,
+ .supportInfo{
+ .usesSessions = true,
+ .boosts = 0,
+ .modes = 0,
+ .sessionHints = -1,
+ .sessionModes = -1,
+ .sessionTags = -1,
+ },
+ };
+
int32_t mMaxLoadHintsPerInterval;
int64_t mLoadHintInterval;
@@ -256,12 +271,6 @@ bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) {
lhs.gpuDurationNanos == rhs.gpuDurationNanos && lhs.durationNanos == rhs.durationNanos;
}
-TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) {
- APerformanceHintManager* manager = createManager();
- int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager);
- EXPECT_EQ(123L, preferredUpdateRateNanos);
-}
-
TEST_F(PerformanceHintTest, TestSession) {
APerformanceHintManager* manager = createManager();
APerformanceHintSession* session = createSession(manager);
@@ -299,6 +308,10 @@ TEST_F(PerformanceHintTest, TestSession) {
EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_EQ(0, result);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_SPIKE))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_SPIKE))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadSpike(session, true, true, "Test hint");
+ EXPECT_EQ(0, result);
result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
EXPECT_EQ(EINVAL, result);
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 7ad8c4c8de41..c33665aef41d 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -37,6 +37,7 @@ filegroup {
java_sdk_library {
name: "framework-nfc",
libs: [
+ "androidx.annotation_annotation",
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
"framework-permission-s.stubs.module_lib",
"framework-permission.stubs.module_lib",
@@ -55,7 +56,7 @@ java_sdk_library {
],
defaults: ["framework-module-defaults"],
sdk_version: "module_current",
- min_sdk_version: "current",
+ min_sdk_version: "35", // Make it 36 once available.
installable: true,
optimize: {
enabled: false,
@@ -70,6 +71,7 @@ java_sdk_library {
"//cts/hostsidetests/multidevices/nfc:__subpackages__",
"//cts/tests/tests/nfc",
"//packages/apps/Nfc:__subpackages__",
+ "//packages/modules/Nfc:__subpackages__",
],
jarjar_rules: ":nfc-jarjar-rules",
lint: {
diff --git a/nfc/OWNERS b/nfc/OWNERS
index 35e9713f5715..f46dccd97974 100644
--- a/nfc/OWNERS
+++ b/nfc/OWNERS
@@ -1,2 +1,2 @@
# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
+include platform/packages/apps/Nfc:/OWNERS \ No newline at end of file
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index e97b15d3b926..6bd6072a2f43 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -85,6 +85,10 @@ package android.nfc {
field public static final int HCE_ACTIVATE = 1; // 0x1
field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2
field public static final int HCE_DEACTIVATE = 3; // 0x3
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_A = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_B = 2; // 0x2
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_F = 4; // 0x4
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_NONE = 0; // 0x0
field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2
field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1
field public static final int STATUS_OK = 0; // 0x0
@@ -196,9 +200,11 @@ package android.nfc {
method @Nullable @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public android.nfc.T4tNdefNfceeCcFileInfo readCcfile();
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public byte[] readData(@IntRange(from=0, to=65535) int);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int writeData(@IntRange(from=0, to=65535) int, @NonNull byte[]);
+ field public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1; // 0xffffffff
field public static final int CLEAR_DATA_FAILED_INTERNAL = 0; // 0x0
field public static final int CLEAR_DATA_SUCCESS = 1; // 0x1
field public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; // 0xfffffffa
+ field public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9; // 0xfffffff7
field public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; // 0xfffffff9
field public static final int WRITE_DATA_ERROR_INTERNAL = -1; // 0xffffffff
field public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; // 0xfffffffc
@@ -213,21 +219,14 @@ package android.nfc {
method public int describeContents();
method @IntRange(from=15, to=32767) public int getCcFileLength();
method @IntRange(from=0xffffffff, to=65535) public int getFileId();
- method @IntRange(from=15, to=65535) public int getMaxReadLength();
method @IntRange(from=5, to=32767) public int getMaxSize();
- method @IntRange(from=13, to=65535) public int getMaxWriteLength();
- method public int getReadAccess();
method public int getVersion();
- method public int getWriteAccess();
+ method public boolean isReadAllowed();
+ method public boolean isWriteAllowed();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.nfc.T4tNdefNfceeCcFileInfo> CREATOR;
- field public static final int READ_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
- field public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
field public static final int VERSION_2_0 = 32; // 0x20
field public static final int VERSION_3_0 = 48; // 0x30
- field public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
- field public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
- field public static final int WRITE_ACCESS_NOT_GRANTED = 255; // 0xff
}
}
@@ -250,6 +249,7 @@ package android.nfc.cardemulation {
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; // 0x1
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; // 0x3
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1; // 0xffffffff
}
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index fb11875ec7d7..b46e34368e77 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -150,28 +150,24 @@ public final class NfcOemExtension {
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_NONE = 0;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_A = 1;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_B = 1 << 1;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_F = 1 << 2;
@@ -670,12 +666,15 @@ public final class NfcOemExtension {
/**
* Get the Active NFCEE (NFC Execution Environment) List
*
- * @see Reader#getName() for the list of possible NFCEE names.
- *
* @return Map< String, @NfceeTechnology Integer >
* A HashMap where keys are activated secure elements and
- * the values are bitmap of technologies supported by each secure element
- * on success keys can contain "eSE" and "UICC", otherwise empty map.
+ * the values are bitmap of technologies supported by each secure element:
+ * NFCEE_TECH_A == 0x1
+ * NFCEE_TECH_B == 0x2
+ * NFCEE_TECH_F == 0x4
+ * and keys can contain "eSE" and "SIM" with a number,
+ * in case of failure an empty map is returned.
+ * @see Reader#getName() for the list of possible NFCEE names.
*/
@NonNull
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
diff --git a/nfc/java/android/nfc/T4tNdefNfcee.java b/nfc/java/android/nfc/T4tNdefNfcee.java
index 06d02c54eb2e..05a30aad76fc 100644
--- a/nfc/java/android/nfc/T4tNdefNfcee.java
+++ b/nfc/java/android/nfc/T4tNdefNfcee.java
@@ -100,9 +100,14 @@ public final class T4tNdefNfcee {
public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7;
/**
* Returns flag for {@link #writeData(int, byte[])}.
- * It idicates write data fail due to invalid ndef format.
+ * It indicates write data fail due to invalid ndef format.
*/
public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8;
+ /**
+ * Returns flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail if a concurrent NDEF NFCEE operation is ongoing.
+ */
+ public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9;
/**
* Possible return values for {@link #writeData(int, byte[])}.
@@ -119,6 +124,7 @@ public final class T4tNdefNfcee {
WRITE_DATA_ERROR_CONNECTION_FAILED,
WRITE_DATA_ERROR_EMPTY_PAYLOAD,
WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED,
+ WRITE_DATA_ERROR_DEVICE_BUSY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteDataStatus{}
@@ -128,6 +134,9 @@ public final class T4tNdefNfcee {
*
* <p>This is an I/O operation and will block until complete. It must
* not be called from the main application thread.</p>
+ * <p>Applications must send complete Ndef Message payload, do not need to fragment
+ * the payload, it will be automatically fragmented and defragmented by
+ * {@link #writeData} if it exceeds max message length limits</p>
*
* @param fileId File id (Refer NFC Forum Type 4 Tag Specification
* Section 4.2 File Identifiers and Access Conditions
@@ -155,9 +164,10 @@ public final class T4tNdefNfcee {
* @param fileId File Id (Refer
* Section 4.2 File Identifiers and Access Conditions
* for more information) from which to read.
- * @return - Returns Ndef message if success
+ * @return - Returns complete Ndef message if success
* Refer to Nfc forum NDEF specification NDEF Message section
- * @throws IllegalStateException if read fails because the fileId is invalid.
+ * @throws IllegalStateException if read fails because the fileId is invalid
+ * or if a concurrent operation is in progress.
* @hide
*/
@SystemApi
@@ -179,6 +189,12 @@ public final class T4tNdefNfcee {
* It indicates clear data failed due to internal error while processing the clear.
*/
public static final int CLEAR_DATA_FAILED_INTERNAL = 0;
+ /**
+ * Return flag for {@link #clearNdefData()}.
+ * It indicates clear data failed if a concurrent NDEF NFCEE operation is ongoing.
+ */
+ public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1;
+
/**
* Possible return values for {@link #clearNdefData()}.
@@ -188,6 +204,7 @@ public final class T4tNdefNfcee {
@IntDef(prefix = { "CLEAR_DATA_" }, value = {
CLEAR_DATA_SUCCESS,
CLEAR_DATA_FAILED_INTERNAL,
+ CLEAR_DATA_FAILED_DEVICE_BUSY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ClearDataStatus{}
@@ -245,6 +262,7 @@ public final class T4tNdefNfcee {
* Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
*
* @return Returns CC file content if success or null if failed to read.
+ * @throws IllegalStateException if the device is busy.
* @hide
*/
@SystemApi
diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
index 5fca0529124e..ce67f8f9aea7 100644
--- a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
+++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
@@ -47,14 +47,6 @@ public final class T4tNdefNfceeCcFileInfo implements Parcelable {
*/
private int mVersion;
/**
- * Indicates the max data size by a single ReadBinary<p>
- */
- private int mMaxReadLength;
- /**
- * Indicates the max data size by a single UpdateBinary<p>
- */
- private int mMaxWriteLength;
- /**
* Indicates the NDEF File Identifier<p>
*/
private int mFileId;
@@ -65,40 +57,35 @@ public final class T4tNdefNfceeCcFileInfo implements Parcelable {
/**
* Indicates the read access condition<p>
*/
- private int mReadAccess;
+ private boolean mIsReadAllowed;
/**
* Indicates the write access condition<p>
*/
- private int mWriteAccess;
+ private boolean mIsWriteAllowed;
/**
* Constructor to be used by NFC service and internal classes.
* @hide
*/
- public T4tNdefNfceeCcFileInfo(int cclen, int version, int maxLe, int maxLc,
+ public T4tNdefNfceeCcFileInfo(int cclen, int version,
int ndefFileId, int ndefMaxSize,
- int ndefReadAccess, int ndefWriteAccess) {
+ boolean isReadAllowed, boolean isWriteAllowed) {
mCcLength = cclen;
mVersion = version;
- mMaxWriteLength = maxLc;
- mMaxReadLength = maxLe;
mFileId = ndefFileId;
mMaxSize = ndefMaxSize;
- mReadAccess = ndefReadAccess;
- mWriteAccess = ndefWriteAccess;
+ mIsReadAllowed = isReadAllowed;
+ mIsWriteAllowed = isWriteAllowed;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
-
dest.writeInt(mCcLength);
dest.writeInt(mVersion);
- dest.writeInt(mMaxWriteLength);
- dest.writeInt(mMaxReadLength);
dest.writeInt(mFileId);
dest.writeInt(mMaxSize);
- dest.writeInt(mReadAccess);
- dest.writeInt(mWriteAccess);
+ dest.writeBoolean(mIsReadAllowed);
+ dest.writeBoolean(mIsWriteAllowed);
}
/**
@@ -146,30 +133,6 @@ public final class T4tNdefNfceeCcFileInfo implements Parcelable {
}
/**
- * Indicates the max data size that can be read by a single invocation of
- * {@link T4tNdefNfcee#readData(int)}.
- *
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLe.
- * @return max size of read (in bytes).
- */
- @IntRange(from = 0xf, to = 0xffff)
- public int getMaxReadLength() {
- return mMaxReadLength;
- }
-
- /**
- * Indicates the max data size that can be written by a single invocation of
- * {@link T4tNdefNfcee#writeData(int, byte[])}
- *
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLc.
- * @return max size of write (in bytes).
- */
- @IntRange(from = 0xd, to = 0xffff)
- public int getMaxWriteLength() {
- return mMaxWriteLength;
- }
-
- /**
* Indicates the NDEF File Identifier. This is the identifier used in the last invocation of
* {@link T4tNdefNfcee#writeData(int, byte[])}
*
@@ -191,73 +154,21 @@ public final class T4tNdefNfceeCcFileInfo implements Parcelable {
}
/**
- * T4T tag read access granted without any security.
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- */
- public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0x0;
- /**
- * T4T tag read access granted with limited proprietary access only.
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- */
- public static final int READ_ACCESS_GRANTED_RESTRICTED = 0x80;
-
- /**
- * Possible return values for {@link #getVersion()}.
- * @hide
- */
- @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
- READ_ACCESS_GRANTED_RESTRICTED,
- READ_ACCESS_GRANTED_UNRESTRICTED,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ReadAccess {}
-
- /**
* Indicates the read access condition.
* Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- * @return read access restriction
+ * @return boolean true if read access is allowed, otherwise false.
*/
- @ReadAccess
- public int getReadAccess() {
- return mReadAccess;
+ public boolean isReadAllowed() {
+ return mIsReadAllowed;
}
/**
- * T4T tag write access granted without any security.
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- */
- public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0x0;
- /**
- * T4T tag write access granted with limited proprietary access only.
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- */
- public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 0x80;
- /**
- * T4T tag write access not granted.
- * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- */
- public static final int WRITE_ACCESS_NOT_GRANTED = 0xFF;
-
- /**
- * Possible return values for {@link #getVersion()}.
- * @hide
- */
- @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
- WRITE_ACCESS_GRANTED_RESTRICTED,
- WRITE_ACCESS_GRANTED_UNRESTRICTED,
- WRITE_ACCESS_NOT_GRANTED,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface WriteAccess {}
-
- /**
* Indicates the write access condition.
* Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
- * @return write access restriction
+ * @return boolean if write access is allowed, otherwise false.
*/
- @WriteAccess
- public int getWriteAccess() {
- return mWriteAccess;
+ public boolean isWriteAllowed() {
+ return mIsWriteAllowed;
}
@Override
@@ -273,16 +184,14 @@ public final class T4tNdefNfceeCcFileInfo implements Parcelable {
// NdefNfceeCcFileInfo fields
int cclen = in.readInt();
int version = in.readInt();
- int maxLe = in.readInt();
- int maxLc = in.readInt();
int ndefFileId = in.readInt();
int ndefMaxSize = in.readInt();
- int ndefReadAccess = in.readInt();
- int ndefWriteAccess = in.readInt();
+ boolean isReadAllowed = in.readBoolean();
+ boolean isWriteAllowed = in.readBoolean();
- return new T4tNdefNfceeCcFileInfo(cclen, version, maxLe, maxLc,
+ return new T4tNdefNfceeCcFileInfo(cclen, version,
ndefFileId, ndefMaxSize,
- ndefReadAccess, ndefWriteAccess);
+ isReadAllowed, isWriteAllowed);
}
@Override
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 308b5d1831a6..7f64dbea0be3 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -174,7 +174,7 @@ public final class ApduServiceInfo implements Parcelable {
* Whether or not this service wants to share the same routing priority as the
* Wallet role owner.
*/
- private boolean mShareRolePriority;
+ private boolean mWantsRoleHolderPriority;
/**
* @hide
@@ -314,8 +314,8 @@ public final class ApduServiceInfo implements Parcelable {
R.styleable.HostApduService_shouldDefaultToObserveMode,
false);
if (Flags.nfcAssociatedRoleServices()) {
- mShareRolePriority = sa.getBoolean(
- R.styleable.HostApduService_shareRolePriority,
+ mWantsRoleHolderPriority = sa.getBoolean(
+ R.styleable.HostApduService_wantsRoleHolderPriority,
false
);
}
@@ -350,8 +350,8 @@ public final class ApduServiceInfo implements Parcelable {
}
mStaticOffHostName = mOffHostName;
if (Flags.nfcAssociatedRoleServices()) {
- mShareRolePriority = sa.getBoolean(
- R.styleable.OffHostApduService_shareRolePriority,
+ mWantsRoleHolderPriority = sa.getBoolean(
+ R.styleable.OffHostApduService_wantsRoleHolderPriority,
false
);
}
@@ -752,8 +752,8 @@ public final class ApduServiceInfo implements Parcelable {
* @return whether or not this service wants to share priority
*/
@FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES)
- public boolean shareRolePriority() {
- return mShareRolePriority;
+ public boolean wantsRoleHolderPriority() {
+ return mWantsRoleHolderPriority;
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 803770218299..baae05b4ea03 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -1114,6 +1114,14 @@ public final class CardEmulation {
@FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3;
+ /**
+ * Setting the default subscription ID failed because of unknown error.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
+ public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1;
+
/** @hide */
@IntDef(prefix = "SET_SUBSCRIPTION_ID_STATUS_",
value = {
@@ -1121,6 +1129,7 @@ public final class CardEmulation {
SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID,
SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR,
SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED,
+ SET_SUBSCRIPTION_ID_STATUS_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
public @interface SetSubscriptionIdStatus {}
@@ -1129,9 +1138,10 @@ public final class CardEmulation {
* Sets the system's default NFC subscription id.
*
* <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this sets the
- * default UICC NFCEE that will handle NFC offhost CE transactoions </p>
+ * default UICC NFCEE that will handle NFC offhost CE transactions </p>
*
- * @param subscriptionId the default NFC subscription Id to set.
+ * @param subscriptionId the default NFC subscription Id to set. User can get subscription id
+ * from {@link SubscriptionManager#getSubscriptionId(int)}
* @return status of the operation.
*
* @throws UnsupportedOperationException If the device does not have
@@ -1153,7 +1163,7 @@ public final class CardEmulation {
* Returns the system's default NFC subscription id.
*
* <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this returns the
- * default UICC NFCEE that will handle NFC offhost CE transactoions </p>
+ * default UICC NFCEE that will handle NFC offhost CE transactions </p>
* <p> If the device has no UICC that can serve as NFCEE, this will return
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.</p>
*
@@ -1317,7 +1327,10 @@ public final class CardEmulation {
/**
* This method is called when an AID conflict is detected during an NFC transaction. This
- * can happen when multiple services are registered for the same AID.
+ * can happen when multiple services are registered for the same AID. If your service is
+ * registered for this AID you may want to instruct users to bring your app to the
+ * foreground and ensure you call {@link #setPreferredService(Activity, ComponentName)}
+ * to ensure the transaction is routed to your service.
*
* @param aid The AID that is in conflict
*/
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index db1f6a2bb3b1..fbf2203b40b4 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -289,7 +289,7 @@ public abstract class HostApduService extends Service {
try {
mNfcService.send(responseMsg);
} catch (RemoteException e) {
- Log.e("TAG", "Response not sent; RemoteException calling into " +
+ Log.e(TAG, "Response not sent; RemoteException calling into " +
"NfcService.");
}
}
diff --git a/nfc/java/android/nfc/cardemulation/OWNERS b/nfc/java/android/nfc/cardemulation/OWNERS
deleted file mode 100644
index 35e9713f5715..000000000000
--- a/nfc/java/android/nfc/cardemulation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/dta/OWNERS b/nfc/java/android/nfc/dta/OWNERS
deleted file mode 100644
index 35e9713f5715..000000000000
--- a/nfc/java/android/nfc/dta/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/tech/OWNERS b/nfc/java/android/nfc/tech/OWNERS
deleted file mode 100644
index 35e9713f5715..000000000000
--- a/nfc/java/android/nfc/tech/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml
index dd7b03de47cd..67b496e0baf3 100644
--- a/nfc/lint-baseline.xml
+++ b/nfc/lint-baseline.xml
@@ -2,215 +2,6 @@
<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.AidGroup`"
- errorLine1=" AidGroup aidGroup = new AidGroup(aids, category);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="377"
- column="29"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
- errorLine1=" return (group != null ? group.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="537"
- column="43"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
- errorLine1=" return (group != null ? group.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="547"
- column="47"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="714"
- column="55"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="724"
- column="59"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
- errorLine1=" if (!serviceInfo.isOnHost()) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="755"
- column="34"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="756"
- column="40"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=' "OffHost" : serviceInfo.getOffHostSecureElement();'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="757"
- column="53"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
- errorLine1=" if (!serviceInfo.isOnHost()) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="772"
- column="38"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="773"
- column="44"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=' "Offhost" : serviceInfo.getOffHostSecureElement();'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="774"
- column="57"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="798"
- column="55"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="808"
- column="59"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="1032"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="1066"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" resumed = activity.isResumed();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/NfcActivityManager.java"
- line="124"
- column="32"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
- line="2457"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
- line="315"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
- line="351"
- column="23"/>
- </issue>
-
- <issue
id="FlaggedApi"
message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)"
errorLine1=" mNfcOemExtension = new NfcOemExtension(mContext, this);"
@@ -287,4 +78,4 @@
column="44"/>
</issue>
-</issues> \ No newline at end of file
+</issues>
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index bfa814d149f0..b6090e853158 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -25,17 +25,36 @@ package {
android_test {
name: "NfcManagerTests",
static_libs: [
- "androidx.test.ext.junit",
+ "androidx.test.core",
"androidx.test.rules",
- "mockito-target-minus-junit4",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "framework-nfc.impl",
+ "mockito-target-extended-minus-junit4",
+ "frameworks-base-testutils",
"truth",
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "flag-junit",
+ "platform-test-annotations",
+ "testables",
],
libs: [
- "framework-nfc.impl",
+ "android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
+ jni_libs: [
+ // Required for ExtendedMockito
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "mts-nfc",
+ ],
+ min_sdk_version: "35", // Should be 36 later.
}
diff --git a/nfc/tests/AndroidManifest.xml b/nfc/tests/AndroidManifest.xml
index 99e2c34c656b..95646720d3d5 100644
--- a/nfc/tests/AndroidManifest.xml
+++ b/nfc/tests/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.nfc">
- <application>
+ <application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java b/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java
new file mode 100644
index 000000000000..7e0010247ee7
--- /dev/null
+++ b/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java
@@ -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 android.nfc.cardemulation;
+
+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.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AidGroupTest {
+ private AidGroup mAidGroup;
+
+ @Before
+ public void setUp() {
+ List<String> aids = new ArrayList<>();
+ aids.add("A0000000031010");
+ aids.add("A0000000041010");
+ aids.add("A0000000034710");
+ aids.add("A000000300");
+ mAidGroup = new AidGroup(aids, "payment");
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void testGetCategory() {
+ String category = mAidGroup.getCategory();
+ assertThat(category).isNotNull();
+ assertThat(category).isEqualTo("payment");
+ }
+
+ @Test
+ public void testGetAids() {
+ List<String> aids = mAidGroup.getAids();
+ assertThat(aids).isNotNull();
+ assertThat(aids.size()).isGreaterThan(0);
+ assertThat(aids.get(0)).isEqualTo("A0000000031010");
+ }
+
+ @Test
+ public void testWriteAsXml() throws IOException {
+ XmlSerializer out = mock(XmlSerializer.class);
+ mAidGroup.writeAsXml(out);
+ verify(out, atLeastOnce()).startTag(isNull(), anyString());
+ verify(out, atLeastOnce()).attribute(isNull(), anyString(), anyString());
+ verify(out, atLeastOnce()).endTag(isNull(), anyString());
+ }
+
+ @Test
+ public void testRightToParcel() {
+ Parcel parcel = mock(Parcel.class);
+ mAidGroup.writeToParcel(parcel, 0);
+ verify(parcel).writeString8(anyString());
+ verify(parcel).writeInt(anyInt());
+ verify(parcel).writeStringList(any());
+ }
+}
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index b266912ca156..2a6d68d1ee35 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -88,6 +88,17 @@
<!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
<string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream apps from your <xliff:g id="device_type" example="phone">%3$s</xliff:g></string>
+ <!-- ================= DEVICE_PROFILE_SENSOR_DEVICE_STREAMING ================= -->
+
+ <!-- Confirmation for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+ <string name="title_sensor_device_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream audio and system features between your <xliff:g id="device_type" example="phone">%2$s</xliff:g> and &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
+
+ <!-- Summary for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile [CHAR LIMIT=NONE] -->
+ <string name="summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s played on your <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream audio to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
+
+ <!-- Description of the helper dialog for SENSOR_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
+ <string name="helper_summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream audio and system features between your devices.</string>
+
<!-- ================= null profile ================= -->
<!-- A noun for a companion device with unspecified profile (type) [CHAR LIMIT=30] -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
index 37b1f297f90b..fd771640ec09 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -21,6 +21,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -116,6 +117,7 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, R.string.title_automotive_projection);
map.put(DEVICE_PROFILE_COMPUTER, R.string.title_computer);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.title_nearby_device_streaming);
+ map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.title_sensor_device_streaming);
map.put(DEVICE_PROFILE_WATCH, R.string.confirmation_title);
map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses);
map.put(null, R.string.confirmation_title);
@@ -130,6 +132,7 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses);
map.put(DEVICE_PROFILE_APP_STREAMING, R.string.summary_app_streaming);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.summary_nearby_device_streaming);
+ map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.summary_sensor_device_streaming);
map.put(null, R.string.summary_generic);
PROFILE_SUMMARIES = unmodifiableMap(map);
@@ -141,6 +144,8 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_APP_STREAMING, R.string.helper_summary_app_streaming);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
R.string.helper_summary_nearby_device_streaming);
+ map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+ R.string.helper_summary_sensor_device_streaming);
map.put(DEVICE_PROFILE_COMPUTER, R.string.helper_summary_computer);
PROFILE_HELPER_SUMMARIES = unmodifiableMap(map);
@@ -204,6 +209,7 @@ final class CompanionDeviceResources {
set.add(DEVICE_PROFILE_COMPUTER);
set.add(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION);
set.add(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+ set.add(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
set.add(null);
SUPPORTED_SELF_MANAGED_PROFILES = unmodifiableSet(set);
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 43d8a628837b..1a3446ec56de 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -14,7 +14,11 @@ java_sdk_library {
name: "framework-platformcrashrecovery",
srcs: [":framework-crashrecovery-sources"],
defaults: ["framework-non-updatable-unbundled-defaults"],
- permitted_packages: ["android.service.watchdog"],
+ permitted_packages: [
+ "android.service.watchdog",
+ "android.crashrecovery",
+ ],
+ static_libs: ["android.crashrecovery.flags-aconfig-java"],
aidl: {
include_dirs: [
"frameworks/base/core/java",
diff --git a/packages/CrashRecovery/framework/api/system-current.txt b/packages/CrashRecovery/framework/api/system-current.txt
index 3a48a4ab02f2..68429ea4297d 100644
--- a/packages/CrashRecovery/framework/api/system-current.txt
+++ b/packages/CrashRecovery/framework/api/system-current.txt
@@ -9,7 +9,9 @@ package android.service.watchdog {
method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
method public abstract void onRequestHealthCheck(@NonNull String);
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public final void setHealthCheckResultCallback(@Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<android.os.Bundle>);
field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+ field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE = "android.service.watchdog.extra.HEALTH_CHECK_PASSED_PACKAGE";
field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
}
diff --git a/packages/CrashRecovery/framework/api/test-current.txt b/packages/CrashRecovery/framework/api/test-current.txt
index 54f501faa250..d802177e249b 100644
--- a/packages/CrashRecovery/framework/api/test-current.txt
+++ b/packages/CrashRecovery/framework/api/test-current.txt
@@ -1,9 +1 @@
// Signature format: 2.0
-package android.service.watchdog {
-
- public abstract class ExplicitHealthCheckService extends android.app.Service {
- method public void setCallback(@Nullable android.os.RemoteCallback);
- }
-
-}
-
diff --git a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
index 7befbfb0f370..b03e37600bbb 100644
--- a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -18,15 +18,17 @@ package android.service.watchdog;
import static android.os.Parcelable.Creator;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.crashrecovery.flags.Flags;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -42,7 +44,9 @@ import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* A service to provide packages supporting explicit health checks and route checks to these
@@ -89,11 +93,10 @@ public abstract class ExplicitHealthCheckService extends Service {
/**
* {@link Bundle} key for a {@link String} value.
- *
- * {@hide}
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
- "android.service.watchdog.extra.health_check_passed_package";
+ "android.service.watchdog.extra.HEALTH_CHECK_PASSED_PACKAGE";
/**
* The Intent action that a service must respond to. Add it to the intent filter of the service
@@ -152,7 +155,8 @@ public abstract class ExplicitHealthCheckService extends Service {
@NonNull public abstract List<String> onGetRequestedPackages();
private final Handler mHandler = Handler.createAsync(Looper.getMainLooper());
- @Nullable private RemoteCallback mCallback;
+ @Nullable private Consumer<Bundle> mHealthCheckResultCallback;
+ @Nullable private Executor mCallbackExecutor;
@Override
@NonNull
@@ -161,30 +165,49 @@ public abstract class ExplicitHealthCheckService extends Service {
}
/**
- * Sets {@link RemoteCallback}, for testing purpose.
+ * Sets a callback to be invoked when an explicit health check passes for a package.
+ * <p>
+ * The callback will receive a {@link Bundle} containing the package name that passed the
+ * health check, identified by the key {@link #EXTRA_HEALTH_CHECK_PASSED_PACKAGE}.
+ * <p>
+ * <b>Note:</b> This API is primarily intended for testing purposes. Calling this outside of a
+ * test environment will override the default callback mechanism used to notify the system
+ * about health check results. Use with caution in production code.
*
- * @hide
+ * @param executor The executor on which the callback should be invoked. If {@code null}, the
+ * callback will be executed on the main thread.
+ * @param callback A callback that receives a {@link Bundle} containing the package name that
+ * passed the health check.
*/
- @TestApi
- public void setCallback(@Nullable RemoteCallback callback) {
- mCallback = callback;
+ @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
+ public final void setHealthCheckResultCallback(@CallbackExecutor @Nullable Executor executor,
+ @Nullable Consumer<Bundle> callback) {
+ mCallbackExecutor = executor;
+ mHealthCheckResultCallback = callback;
}
+
+ private void executeCallback(@NonNull String packageName) {
+ if (mHealthCheckResultCallback != null) {
+ Objects.requireNonNull(packageName,
+ "Package passing explicit health check must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
+ mHealthCheckResultCallback.accept(bundle);
+ } else {
+ Log.wtf(TAG, "System missed explicit health check result for " + packageName);
+ }
+ }
+
/**
* Implementors should call this to notify the system when explicit health check passes
* for {@code packageName};
*/
public final void notifyHealthCheckPassed(@NonNull String packageName) {
- mHandler.post(() -> {
- if (mCallback != null) {
- Objects.requireNonNull(packageName,
- "Package passing explicit health check must be non-null");
- Bundle bundle = new Bundle();
- bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
- mCallback.sendResult(bundle);
- } else {
- Log.wtf(TAG, "System missed explicit health check result for " + packageName);
- }
- });
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> executeCallback(packageName));
+ } else {
+ mHandler.post(() -> executeCallback(packageName));
+ }
}
/**
@@ -296,9 +319,7 @@ public abstract class ExplicitHealthCheckService extends Service {
private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
@Override
public void setCallback(RemoteCallback callback) throws RemoteException {
- mHandler.post(() -> {
- mCallback = callback;
- });
+ mHandler.post(() -> mHealthCheckResultCallback = callback::sendResult);
}
@Override
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 2ba93f15f7fc..8b8ab587e84a 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -25,6 +25,7 @@ import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecov
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -91,6 +92,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -336,20 +338,27 @@ public class PackageWatchdog {
/**
* Registers {@code observer} to listen for package failures. Add a new ObserverInternal for
* this observer if it does not already exist.
+ * For executing mitigations observers will receive callback on the given executor.
*
* <p>Observers are expected to call this on boot. It does not specify any packages but
* it will resume observing any packages requested from a previous boot.
- * @hide
+ *
+ * @param observer instance of {@link PackageHealthObserver} for observing package failures
+ * and boot loops.
+ * @param executor Executor for the thread on which observers would receive callbacks
*/
- public void registerHealthObserver(PackageHealthObserver observer) {
+ public void registerHealthObserver(@NonNull PackageHealthObserver observer,
+ @NonNull @CallbackExecutor Executor executor) {
synchronized (sLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
+ internalObserver.observerExecutor = executor;
} else {
internalObserver = new ObserverInternal(observer.getUniqueIdentifier(),
new ArrayList<>());
internalObserver.registeredObserver = observer;
+ internalObserver.observerExecutor = executor;
mAllObservers.put(observer.getUniqueIdentifier(), internalObserver);
syncState("added new observer");
}
@@ -357,40 +366,53 @@ public class PackageWatchdog {
}
/**
- * Starts observing the health of the {@code packages} for {@code observer} and notifies
- * {@code observer} of any package failures within the monitoring duration.
+ * Starts observing the health of the {@code packages} for {@code observer}.
+ * Note: Observer needs to be registered with {@link #registerHealthObserver} before calling
+ * this API.
*
* <p>If monitoring a package supporting explicit health check, at the end of the monitoring
* duration if {@link #onHealthCheckPassed} was never called,
- * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the package failed.
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the
+ * package failed.
*
* <p>If {@code observer} is already monitoring a package in {@code packageNames},
* the monitoring window of that package will be reset to {@code durationMs} and the health
- * check state will be reset to a default depending on if the package is contained in
- * {@link mPackagesWithExplicitHealthCheckEnabled}.
+ * check state will be reset to a default.
*
- * <p>If {@code packageNames} is empty, this will be a no-op.
+ * <p>The {@code observer} must be registered with {@link #registerHealthObserver} before
+ * calling this method.
*
- * <p>If {@code durationMs} is less than 1, a default monitoring duration
- * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
- * @hide
+ * @param packageNames The list of packages to check. If this is empty, the call will be a
+ * no-op.
+ *
+ * @param timeoutMs The timeout after which Explicit Health Checks would not run. If this is
+ * less than 1, a default monitoring duration 2 days will be used.
+ *
+ * @throws IllegalStateException if the observer was not previously registered
*/
- public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
- long durationMs) {
+ public void startExplicitHealthCheck(@NonNull PackageHealthObserver observer,
+ @NonNull List<String> packageNames, long timeoutMs) {
+ synchronized (sLock) {
+ if (!mAllObservers.containsKey(observer.getUniqueIdentifier())) {
+ Slog.wtf(TAG, "No observer found, need to register the observer: "
+ + observer.getUniqueIdentifier());
+ throw new IllegalStateException("Observer not registered");
+ }
+ }
if (packageNames.isEmpty()) {
Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
return;
}
- if (durationMs < 1) {
- Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
+ if (timeoutMs < 1) {
+ Slog.wtf(TAG, "Invalid duration " + timeoutMs + "ms for observer "
+ observer.getUniqueIdentifier() + ". Not observing packages " + packageNames);
- durationMs = DEFAULT_OBSERVING_DURATION_MS;
+ timeoutMs = DEFAULT_OBSERVING_DURATION_MS;
}
List<MonitoredPackage> packages = new ArrayList<>();
for (int i = 0; i < packageNames.size(); i++) {
// Health checks not available yet so health check state will start INACTIVE
- MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false);
+ MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), timeoutMs, false);
if (pkg != null) {
packages.add(pkg);
} else {
@@ -423,9 +445,6 @@ public class PackageWatchdog {
}
}
- // Register observer in case not already registered
- registerHealthObserver(observer);
-
// Sync after we add the new packages to the observers. We may have received packges
// requiring an earlier schedule than we are currently scheduled for.
syncState("updated observers");
@@ -437,9 +456,8 @@ public class PackageWatchdog {
* Unregisters {@code observer} from listening to package failure.
* Additionally, this stops observing any packages that may have previously been observed
* even from a previous boot.
- * @hide
*/
- public void unregisterHealthObserver(PackageHealthObserver observer) {
+ public void unregisterHealthObserver(@NonNull PackageHealthObserver observer) {
mLongTaskHandler.post(() -> {
synchronized (sLock) {
mAllObservers.remove(observer.getUniqueIdentifier());
@@ -485,7 +503,7 @@ public class PackageWatchdog {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
VersionedPackage versionedPackage = packages.get(pIndex);
// Observer that will receive failure for versionedPackage
- PackageHealthObserver currentObserverToNotify = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
MonitoredPackage currentMonitoredPackage = null;
@@ -506,7 +524,7 @@ public class PackageWatchdog {
versionedPackage, failureReason, mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
currentMonitoredPackage = p;
}
@@ -515,18 +533,23 @@ public class PackageWatchdog {
// Execute action with least user impact
if (currentObserverToNotify != null) {
- int mitigationCount = 1;
+ int mitigationCount;
if (currentMonitoredPackage != null) {
currentMonitoredPackage.noteMitigationCallLocked();
mitigationCount =
currentMonitoredPackage.getMitigationCountLocked();
+ } else {
+ mitigationCount = 1;
}
if (Flags.recoverabilityDetection()) {
maybeExecute(currentObserverToNotify, versionedPackage,
failureReason, currentObserverImpact, mitigationCount);
} else {
- currentObserverToNotify.onExecuteHealthCheckMitigation(
- versionedPackage, failureReason, mitigationCount);
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(
+ versionedPackage, failureReason, mitigationCount));
}
}
}
@@ -539,10 +562,11 @@ public class PackageWatchdog {
* For native crashes or explicit health check failures, call directly into each observer to
* mitigate the error without going through failure threshold logic.
*/
+ @GuardedBy("sLock")
private void handleFailureImmediately(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null;
- PackageHealthObserver currentObserverToNotify = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
for (ObserverInternal observer: mAllObservers.values()) {
PackageHealthObserver registeredObserver = observer.registeredObserver;
@@ -551,7 +575,7 @@ public class PackageWatchdog {
failingPackage, failureReason, 1);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
}
}
@@ -561,23 +585,30 @@ public class PackageWatchdog {
maybeExecute(currentObserverToNotify, failingPackage, failureReason,
currentObserverImpact, /*mitigationCount=*/ 1);
} else {
- currentObserverToNotify.onExecuteHealthCheckMitigation(failingPackage,
- failureReason, 1);
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(failingPackage,
+ failureReason, 1));
+
}
}
}
- private void maybeExecute(PackageHealthObserver currentObserverToNotify,
+ private void maybeExecute(ObserverInternal currentObserverToNotify,
VersionedPackage versionedPackage,
@FailureReasons int failureReason,
int currentObserverImpact,
int mitigationCount) {
if (allowMitigations(currentObserverImpact, versionedPackage)) {
+ PackageHealthObserver registeredObserver;
synchronized (sLock) {
mLastMitigation = mSystemClock.uptimeMillis();
+ registeredObserver = currentObserverToNotify.registeredObserver;
}
- currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason,
- mitigationCount);
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(versionedPackage,
+ failureReason, mitigationCount));
}
}
@@ -613,8 +644,7 @@ public class PackageWatchdog {
mBootThreshold.reset();
}
int mitigationCount = mBootThreshold.getMitigationCount() + 1;
- PackageHealthObserver currentObserverToNotify = null;
- ObserverInternal currentObserverInternal = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
for (int i = 0; i < mAllObservers.size(); i++) {
final ObserverInternal observer = mAllObservers.valueAt(i);
@@ -626,25 +656,31 @@ public class PackageWatchdog {
: registeredObserver.onBootLoop(mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
- currentObserverInternal = observer;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
}
}
}
+
if (currentObserverToNotify != null) {
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
if (Flags.recoverabilityDetection()) {
int currentObserverMitigationCount =
- currentObserverInternal.getBootMitigationCount() + 1;
- currentObserverInternal.setBootMitigationCount(
+ currentObserverToNotify.getBootMitigationCount() + 1;
+ currentObserverToNotify.setBootMitigationCount(
currentObserverMitigationCount);
saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
- currentObserverToNotify.onExecuteBootLoopMitigation(
- currentObserverMitigationCount);
+ currentObserverToNotify.observerExecutor
+ .execute(() -> registeredObserver.onExecuteBootLoopMitigation(
+ currentObserverMitigationCount));
} else {
mBootThreshold.setMitigationCount(mitigationCount);
mBootThreshold.saveMitigationCountToMetadata();
- currentObserverToNotify.onExecuteBootLoopMitigation(mitigationCount);
+ currentObserverToNotify.observerExecutor
+ .execute(() -> registeredObserver.onExecuteBootLoopMitigation(
+ mitigationCount));
+
}
}
}
@@ -734,19 +770,19 @@ public class PackageWatchdog {
* The minimum value that can be returned by any observer.
* It represents that no mitigations were available.
*/
- public static final int LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT =
+ public static final int USER_IMPACT_THRESHOLD_NONE =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
/**
* The mitigation impact beyond which the user will start noticing the mitigations.
*/
- public static final int MEDIUM_USER_IMPACT_THRESHOLD =
+ public static final int USER_IMPACT_THRESHOLD_MEDIUM =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_20;
/**
* The mitigation impact beyond which the user impact is severely high.
*/
- public static final int HIGH_USER_IMPACT_THRESHOLD =
+ public static final int USER_IMPACT_THRESHOLD_HIGH =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
/**
@@ -791,11 +827,9 @@ public class PackageWatchdog {
public interface PackageHealthObserver {
/**
* Called when health check fails for the {@code versionedPackage}.
- *
- * Note: if the returned user impact is higher than
- * {@link #DEFAULT_HIGH_USER_IMPACT_THRESHOLD}, then
- * {@link #onExecuteHealthCheckMitigation} would be called only in severe device conditions
- * like boot-loop or network failure.
+ * Note: if the returned user impact is higher than {@link #USER_IMPACT_THRESHOLD_HIGH},
+ * then {@link #onExecuteHealthCheckMitigation} would be called only in severe device
+ * conditions like boot-loop or network failure.
*
* @param versionedPackage the package that is failing. This may be null if a native
* service is crashing.
@@ -803,9 +837,9 @@ public class PackageWatchdog {
* @param mitigationCount the number of times mitigation has been called for this package
* (including this time).
*
- *
- * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
- * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation}
+ * @return any value greater than {@link #USER_IMPACT_THRESHOLD_NONE} to express
+ * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation}.
+ * Returning {@link #USER_IMPACT_THRESHOLD_NONE} would indicate no mitigations available.
*/
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@@ -835,8 +869,9 @@ public class PackageWatchdog {
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
*
- * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
- * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation}
+ * @return any value greater than {@link #USER_IMPACT_THRESHOLD_NONE} to express
+ * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation}.
+ * Returning {@link #USER_IMPACT_THRESHOLD_NONE} would indicate no mitigations available.
*/
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
@@ -879,7 +914,7 @@ public class PackageWatchdog {
* passed to observers in these API.
*
* <p> A persistent observer may choose to start observing certain failing packages, even if
- * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+ * it has not explicitly asked to watch the package with {@link #startExplicitHealthCheck}.
*/
default boolean mayObservePackage(@NonNull String packageName) {
return false;
@@ -1136,8 +1171,10 @@ public class PackageWatchdog {
if (versionedPkg != null) {
Slog.i(TAG,
"Explicit health check failed for package " + versionedPkg);
- registeredObserver.onExecuteHealthCheckMitigation(versionedPkg,
- PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
+ observer.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(versionedPkg,
+ PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK,
+ 1));
}
}
}
@@ -1395,6 +1432,7 @@ public class PackageWatchdog {
@Nullable
@GuardedBy("sLock")
public PackageHealthObserver registeredObserver;
+ public Executor observerExecutor;
private int mMitigationCount;
ObserverInternal(String name, List<MonitoredPackage> packages) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index 992f581a8a70..bad6ab7c1dd4 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -160,7 +160,7 @@ public class RescueParty {
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context));
+ RescuePartyObserver.getInstance(context), context.getMainExecutor());
}
private static boolean isDisabled() {
@@ -313,7 +313,7 @@ public class RescueParty {
callingPackageList.addAll(callingPackages);
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
- PackageWatchdog.getInstance(context).startObservingHealth(
+ PackageWatchdog.getInstance(context).startExplicitHealthCheck(
rescuePartyObserver,
callingPackageList,
DEFAULT_OBSERVING_DURATION_MS);
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 311def80f248..c80a1a4ea187 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -111,7 +111,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this,
+ context.getMainExecutor());
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
// Load the value from the file if system server has crashed and restarted
@@ -273,16 +274,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
Preconditions.checkState(mHandler.getLooper().isCurrentThread());
}
- /**
- * Start observing health of {@code packages} for {@code durationMs}.
- * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
- */
- @AnyThread
- @NonNull
- public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
- PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
- }
-
@AnyThread
@NonNull
public void notifyRollbackAvailable(@NonNull RollbackInfo rollback) {
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 88fe36cda395..4fea9372971d 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -87,6 +87,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -362,7 +363,7 @@ public class PackageWatchdog {
* it will resume observing any packages requested from a previous boot.
* @hide
*/
- public void registerHealthObserver(PackageHealthObserver observer) {
+ public void registerHealthObserver(PackageHealthObserver observer, Executor ignoredExecutor) {
synchronized (mLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
@@ -396,7 +397,7 @@ public class PackageWatchdog {
* {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
* @hide
*/
- public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
+ public void startExplicitHealthCheck(PackageHealthObserver observer, List<String> packageNames,
long durationMs) {
if (packageNames.isEmpty()) {
Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
@@ -445,7 +446,7 @@ public class PackageWatchdog {
}
// Register observer in case not already registered
- registerHealthObserver(observer);
+ registerHealthObserver(observer, null);
// Sync after we add the new packages to the observers. We may have received packges
// requiring an earlier schedule than we are currently scheduled for.
@@ -861,7 +862,7 @@ public class PackageWatchdog {
* otherwise
*
* <p> A persistent observer may choose to start observing certain failing packages, even if
- * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+ * it has not explicitly asked to watch the package with {@link #startExplicitHealthCheck}.
*/
default boolean mayObservePackage(@NonNull String packageName) {
return false;
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index f757236613d1..2bb72fb43dff 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -166,7 +166,7 @@ public class RescueParty {
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context));
+ RescuePartyObserver.getInstance(context), null);
}
private static boolean isDisabled() {
@@ -387,7 +387,7 @@ public class RescueParty {
callingPackageList.addAll(callingPackages);
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
- PackageWatchdog.getInstance(context).startObservingHealth(
+ PackageWatchdog.getInstance(context).startExplicitHealthCheck(
rescuePartyObserver,
callingPackageList,
DEFAULT_OBSERVING_DURATION_MS);
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 7445534e95bf..0692cdbc5e40 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -110,7 +110,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this, null);
mApexManager = apexManager;
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
@@ -284,7 +284,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
@AnyThread
@NonNull
public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
- PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
+ PackageWatchdog.getInstance(mContext).startExplicitHealthCheck(this, packages, durationMs);
}
@AnyThread
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_middle.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_middle.xml
index 781373dfbd0b..327f42209cdb 100644
--- a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_middle.xml
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_middle.xml
@@ -19,6 +19,6 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape>
</inset> \ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
index f28c354b898d..992fe3341f26 100644
--- a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
@@ -21,6 +21,6 @@
<shape android:shape="rectangle">
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp"
android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape>
</inset> \ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/more_options_list_item.xml b/packages/CredentialManager/res/drawable/more_options_list_item.xml
index 3f9d8157bfd2..991b3ff3c440 100644
--- a/packages/CredentialManager/res/drawable/more_options_list_item.xml
+++ b/packages/CredentialManager/res/drawable/more_options_list_item.xml
@@ -24,7 +24,7 @@
<shape>
<corners android:topLeftRadius="0dp" android:topRightRadius="0dp"
android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer"/>
- <stroke android:color="?androidprv:attr/materialColorOutlineVariant" android:width="1dp"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer"/>
+ <stroke android:color="@androidprv:color/materialColorOutlineVariant" android:width="1dp"/>
</shape>
</inset> \ No newline at end of file
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
index 914987ac4650..6f04bd99877e 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
@@ -35,7 +35,7 @@
android:layout_gravity="center"
android:paddingStart="@dimen/autofill_view_left_padding"
android:src="@drawable/more_horiz_24px"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:contentDescription="@string/more_options_content_description"
android:background="@null"/>
@@ -53,7 +53,7 @@
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
style="@style/autofill.TextTitle"/>
</LinearLayout>
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
index 3fc61545a713..d00a2295e43a 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
@@ -35,7 +35,7 @@
android:layout_gravity="center"
android:layout_alignParentStart="true"
android:paddingStart="@dimen/autofill_view_left_padding"
- app:tint="?androidprv:attr/materialColorOnSurface"
+ app:tint="@androidprv:color/materialColorOnSurface"
android:background="@null"/>
<LinearLayout
@@ -53,7 +53,7 @@
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textDirection="locale"
style="@style/autofill.TextTitle"/>
@@ -61,7 +61,7 @@
android:id="@android:id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textDirection="locale"
style="@style/autofill.TextSubtitle"/>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 630a08a7e903..799712710196 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -22,7 +22,7 @@
<string name="string_continue" msgid="1346732695941131882">"Շարունակել"</string>
<string name="string_more_options" msgid="2763852250269945472">"Պահել մեկ այլ եղանակ"</string>
<string name="string_learn_more" msgid="4541600451688392447">"Իմանալ ավելին"</string>
- <string name="content_description_show_password" msgid="3283502010388521607">"Ցուցադրել գաղտնաբառը"</string>
+ <string name="content_description_show_password" msgid="3283502010388521607">"Ցույց տալ գաղտնաբառը"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"Թաքցնել գաղտնաբառը"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Մուտքի բանալիների հետ ավելի ապահով է"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Մուտքի բանալիների շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
index 9d31b350b4a0..8d37d47cc9fe 100644
--- a/packages/CredentialManager/res/values/colors.xml
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -19,8 +19,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- These colors are used for Remote Views. -->
- <color name="onSurface">?androidprv:attr/materialColorOnSurface</color>
- <color name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</color>
- <color name="surfaceDim">?androidprv:attr/materialColorSurfaceDim</color>
- <color name="surfaceContainer">?androidprv:attr/materialColorSurfaceContainer</color>
+ <color name="onSurface">@androidprv:color/materialColorOnSurface</color>
+ <color name="onSurfaceVariant">@androidprv:color/materialColorOnSurfaceVariant</color>
+ <color name="surfaceDim">@androidprv:color/materialColorSurfaceDim</color>
+ <color name="surfaceContainer">@androidprv:color/materialColorSurfaceContainer</color>
</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/wear/res/values-hi/strings.xml b/packages/CredentialManager/wear/res/values-hi/strings.xml
index a061453e72a4..405d3f8bce18 100644
--- a/packages/CredentialManager/wear/res/values-hi/strings.xml
+++ b/packages/CredentialManager/wear/res/values-hi/strings.xml
@@ -22,8 +22,8 @@
<string name="use_password_title" msgid="4655101984031246476">"क्या आपको पासवर्ड का इस्तेमाल करना है?"</string>
<string name="dialog_dismiss_button" msgid="989567669882005067">"खारिज करें"</string>
<string name="dialog_continue_button" msgid="8630290044077052145">"जारी रखें"</string>
- <string name="dialog_sign_in_options_button" msgid="448002958902615054">"साइन इन करने के विकल्प"</string>
- <string name="sign_in_options_title" msgid="6720572645638986680">"साइन इन करने के विकल्प"</string>
+ <string name="dialog_sign_in_options_button" msgid="448002958902615054">"साइन-इन करने के विकल्प"</string>
+ <string name="sign_in_options_title" msgid="6720572645638986680">"साइन-इन करने के विकल्प"</string>
<string name="provider_list_title" msgid="6803918216129492212">"साइन-इन क्रेडेंशियल मैनेज करें"</string>
<string name="choose_sign_in_title" msgid="3616025924746872202">"साइन इन करने का कोई तरीका चुनें"</string>
<string name="choose_passkey_title" msgid="8459270617632817465">"कोई पासकी चुनें"</string>
diff --git a/packages/CredentialManager/wear/res/values-ta/strings.xml b/packages/CredentialManager/wear/res/values-ta/strings.xml
index 9f88c81b45c0..498d83bcbb07 100644
--- a/packages/CredentialManager/wear/res/values-ta/strings.xml
+++ b/packages/CredentialManager/wear/res/values-ta/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="7384524142163511792">"அனுமதிச் சான்று நிர்வாகி"</string>
<string name="use_passkey_title" msgid="716598039340757817">"கடவுச்சாவியைப் பயன்படுத்தவா?"</string>
- <string name="use_password_title" msgid="4655101984031246476">"கடவுச்சொல்லைப் பயன்படுத்தவா?"</string>
+ <string name="use_password_title" msgid="4655101984031246476">"கடவுச்சொல் பயன்படுத்தவா?"</string>
<string name="dialog_dismiss_button" msgid="989567669882005067">"மூடு"</string>
<string name="dialog_continue_button" msgid="8630290044077052145">"தொடர்க"</string>
<string name="dialog_sign_in_options_button" msgid="448002958902615054">"உள்நுழைவு விருப்பங்கள்"</string>
diff --git a/packages/CredentialManager/wear/res/values-te/strings.xml b/packages/CredentialManager/wear/res/values-te/strings.xml
index 086b109b1933..ff85a89989a6 100644
--- a/packages/CredentialManager/wear/res/values-te/strings.xml
+++ b/packages/CredentialManager/wear/res/values-te/strings.xml
@@ -23,7 +23,7 @@
<string name="dialog_dismiss_button" msgid="989567669882005067">"విస్మరించండి"</string>
<string name="dialog_continue_button" msgid="8630290044077052145">"కొనసాగించండి"</string>
<string name="dialog_sign_in_options_button" msgid="448002958902615054">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
- <string name="sign_in_options_title" msgid="6720572645638986680">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
+ <string name="sign_in_options_title" msgid="6720572645638986680">"సైన్-ఇన్ ఆప్షన్‌లు"</string>
<string name="provider_list_title" msgid="6803918216129492212">"సైన్‌ ఇన్‌లను మేనేజ్ చేయండి"</string>
<string name="choose_sign_in_title" msgid="3616025924746872202">"సైన్ ఇన్‌ను ఎంచుకోండి"</string>
<string name="choose_passkey_title" msgid="8459270617632817465">"పాస్-కీని ఎంచుకోండి"</string>
diff --git a/packages/NeuralNetworks/framework/Android.bp b/packages/NeuralNetworks/framework/Android.bp
new file mode 100644
index 000000000000..af071ba48b1f
--- /dev/null
+++ b/packages/NeuralNetworks/framework/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "framework-ondeviceintelligence-sources",
+ srcs: [
+ "module/java/**/*.aidl",
+ "module/java/**/*.java",
+ ],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
+ ],
+}
+
+filegroup {
+ name: "framework-ondeviceintelligence-sources-platform",
+ srcs: [
+ "platform/java/**/*.aidl",
+ "platform/java/**/*.java",
+ ],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
+ ],
+}
diff --git a/core/java/android/app/ondeviceintelligence/DownloadCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/DownloadCallback.java
index 30c6e1924942..95fb2888a3e9 100644
--- a/core/java/android/app/ondeviceintelligence/DownloadCallback.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/DownloadCallback.java
@@ -23,8 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.PersistableBundle;
-
-import androidx.annotation.IntDef;
+import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,14 +52,14 @@ public interface DownloadCallback {
/**
* Sent when feature download has been initiated already, hence no need to request download
- * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check if
+ * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check if
* download has been completed.
*/
int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3;
/**
* Sent when feature download did not start due to errors (e.g. remote exception of features not
- * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check
+ * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check
* if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}.
*/
int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4;
@@ -72,7 +71,7 @@ public interface DownloadCallback {
DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE,
DOWNLOAD_FAILURE_STATUS_DOWNLOADING,
DOWNLOAD_FAILURE_STATUS_UNAVAILABLE
- }, open = true)
+ })
@Retention(RetentionPolicy.SOURCE)
@interface DownloadFailureStatus {
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.aidl
index 1450dc0803d6..47cfb4a60dc4 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.aidl
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-package com.android.server.ondeviceintelligence;
+package android.app.ondeviceintelligence;
-public interface OnDeviceIntelligenceManagerInternal {
- /**
- * Gets the uid for the process that is currently hosting the
- * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
- * the device.
- */
- int getInferenceServiceUid();
-} \ No newline at end of file
+/**
+ * @hide
+ */
+@JavaOnlyStableParcelable parcelable Feature;
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java
new file mode 100644
index 000000000000..88f4de2989e4
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java
@@ -0,0 +1,267 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.Objects;
+
+/**
+ * Represents a typical feature associated with on-device intelligence.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Feature implements Parcelable {
+ private final int mId;
+ @Nullable
+ private final String mName;
+ @Nullable
+ private final String mModelName;
+ private final int mType;
+ private final int mVariant;
+ @NonNull
+ private final PersistableBundle mFeatureParams;
+
+ /* package-private */ Feature(
+ int id,
+ @Nullable String name,
+ @Nullable String modelName,
+ int type,
+ int variant,
+ @NonNull PersistableBundle featureParams) {
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = Objects.requireNonNull(featureParams,
+ "featureParams should be non-null.");
+ }
+
+ /** Returns the unique and immutable identifier of this feature. */
+ public int getId() {
+ return mId;
+ }
+
+ /** Returns human-readable name of this feature. */
+ public @Nullable String getName() {
+ return mName;
+ }
+
+ /** Returns base model name of this feature. */
+ public @Nullable String getModelName() {
+ return mModelName;
+ }
+
+ /** Returns type identifier of this feature. */
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns variant kind for this feature. */
+ public int getVariant() {
+ return mVariant;
+ }
+
+ public @NonNull PersistableBundle getFeatureParams() {
+ return mFeatureParams;
+ }
+
+ @Override
+ public String toString() {
+ return "Feature { " +
+ "id = " + mId + ", " +
+ "name = " + mName + ", " +
+ "modelName = " + mModelName + ", " +
+ "type = " + mType + ", " +
+ "variant = " + mVariant + ", " +
+ "featureParams = " + mFeatureParams +
+ " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ Feature that = (Feature) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mId == that.mId
+ && java.util.Objects.equals(mName, that.mName)
+ && java.util.Objects.equals(mModelName, that.mModelName)
+ && mType == that.mType
+ && mVariant == that.mVariant
+ && java.util.Objects.equals(mFeatureParams, that.mFeatureParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mModelName);
+ _hash = 31 * _hash + mType;
+ _hash = 31 * _hash + mVariant;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ byte flg = 0;
+ if (mName != null) flg |= 0x2;
+ if (mModelName != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeInt(mId);
+ if (mName != null) dest.writeString(mName);
+ if (mModelName != null) dest.writeString(mModelName);
+ dest.writeInt(mType);
+ dest.writeInt(mVariant);
+ dest.writeTypedObject(mFeatureParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */ Feature(@NonNull Parcel in) {
+ byte flg = in.readByte();
+ int id = in.readInt();
+ String name = (flg & 0x2) == 0 ? null : in.readString();
+ String modelName = (flg & 0x4) == 0 ? null : in.readString();
+ int type = in.readInt();
+ int variant = in.readInt();
+ PersistableBundle featureParams = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = featureParams;
+ }
+
+ public static final @NonNull Parcelable.Creator<Feature> CREATOR
+ = new Parcelable.Creator<Feature>() {
+ @Override
+ public Feature[] newArray(int size) {
+ return new Feature[size];
+ }
+
+ @Override
+ public Feature createFromParcel(@NonNull Parcel in) {
+ return new Feature(in);
+ }
+ };
+
+ /**
+ * A builder for {@link Feature}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private int mId;
+ private @Nullable String mName;
+ private @Nullable String mModelName;
+ private int mType;
+ private int mVariant;
+ private @NonNull PersistableBundle mFeatureParams;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Provides a builder instance to create a feature for given id.
+ *
+ * @param id the unique identifier for the feature.
+ */
+ public Builder(int id) {
+ mId = id;
+ mFeatureParams = new PersistableBundle();
+ }
+
+ public @NonNull Builder setName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mName = value;
+ return this;
+ }
+
+ public @NonNull Builder setModelName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mModelName = value;
+ return this;
+ }
+
+ public @NonNull Builder setType(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mType = value;
+ return this;
+ }
+
+ public @NonNull Builder setVariant(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mVariant = value;
+ return this;
+ }
+
+ public @NonNull Builder setFeatureParams(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mFeatureParams = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull Feature build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40; // Mark builder used
+
+ Feature o = new Feature(
+ mId,
+ mName,
+ mModelName,
+ mType,
+ mVariant,
+ mFeatureParams);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl
new file mode 100644
index 000000000000..c5b3532796cd
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+@JavaOnlyStableParcelable parcelable FeatureDetails;
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java
new file mode 100644
index 000000000000..063cfb8c321e
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -0,0 +1,169 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.text.MessageFormat;
+import java.util.Objects;
+
+/**
+ * Represents a status of a requested {@link Feature}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class FeatureDetails implements Parcelable {
+ @Status
+ private final int mFeatureStatus;
+ @NonNull
+ private final PersistableBundle mFeatureDetailParams;
+
+ /** Invalid or unavailable {@code AiFeature}. */
+ public static final int FEATURE_STATUS_UNAVAILABLE = 0;
+
+ /** Feature can be downloaded on request. */
+ public static final int FEATURE_STATUS_DOWNLOADABLE = 1;
+
+ /** Feature is being downloaded. */
+ public static final int FEATURE_STATUS_DOWNLOADING = 2;
+
+ /** Feature is fully downloaded and ready to use. */
+ public static final int FEATURE_STATUS_AVAILABLE = 3;
+
+ /** Underlying service is unavailable and feature status cannot be fetched. */
+ public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ FEATURE_STATUS_UNAVAILABLE,
+ FEATURE_STATUS_DOWNLOADABLE,
+ FEATURE_STATUS_DOWNLOADING,
+ FEATURE_STATUS_AVAILABLE,
+ FEATURE_STATUS_SERVICE_UNAVAILABLE
+ })
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {
+ }
+
+ public FeatureDetails(
+ @Status int featureStatus,
+ @NonNull PersistableBundle featureDetailParams) {
+ this.mFeatureStatus = featureStatus;
+ this.mFeatureDetailParams = Objects.requireNonNull(featureDetailParams);
+ }
+
+ public FeatureDetails(
+ @Status int featureStatus) {
+ this.mFeatureStatus = featureStatus;
+ this.mFeatureDetailParams = new PersistableBundle();
+ }
+
+
+ /**
+ * Returns an integer value associated with the feature status.
+ */
+ public @Status int getFeatureStatus() {
+ return mFeatureStatus;
+ }
+
+
+ /**
+ * Returns a persistable bundle contain any additional status related params.
+ */
+ public @NonNull PersistableBundle getFeatureDetailParams() {
+ return mFeatureDetailParams;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("FeatureDetails '{' status = {0}, "
+ + "persistableBundle = {1} '}'",
+ mFeatureStatus,
+ mFeatureDetailParams);
+ }
+
+ @Override
+ public boolean equals(@android.annotation.Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FeatureDetails that = (FeatureDetails) o;
+ return mFeatureStatus == that.mFeatureStatus
+ && java.util.Objects.equals(mFeatureDetailParams, that.mFeatureDetailParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mFeatureStatus;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureDetailParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mFeatureStatus);
+ dest.writeTypedObject(mFeatureDetailParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ FeatureDetails(@NonNull android.os.Parcel in) {
+ int status = in.readInt();
+ PersistableBundle persistableBundle = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mFeatureStatus = status;
+ this.mFeatureDetailParams = persistableBundle;
+ }
+
+
+ public static final @NonNull Parcelable.Creator<FeatureDetails> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public FeatureDetails[] newArray(int size) {
+ return new FeatureDetails[size];
+ }
+
+ @Override
+ public FeatureDetails createFromParcel(@NonNull android.os.Parcel in) {
+ return new FeatureDetails(in);
+ }
+ };
+
+}
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
new file mode 100644
index 000000000000..1fe201f8f1f8
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+oneway interface ICancellationSignal {
+ void cancel();
+}
diff --git a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
index 2d7ea1a7b016..2d7ea1a7b016 100644
--- a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
index 2e056926e400..2e056926e400 100644
--- a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
index 8688028743d7..8688028743d7 100644
--- a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
index 7e5eb57bbc4a..7e5eb57bbc4a 100644
--- a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
new file mode 100644
index 000000000000..fac5ec6064f8
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -0,0 +1,79 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+ import com.android.modules.utils.AndroidFuture;
+ import android.app.ondeviceintelligence.ICancellationSignal;
+ import android.os.ParcelFileDescriptor;
+ import android.os.PersistableBundle;
+ import android.os.RemoteCallback;
+ import android.os.Bundle;
+ import android.app.ondeviceintelligence.Feature;
+ import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.InferenceInfo;
+ import java.util.List;
+ import android.app.ondeviceintelligence.IDownloadCallback;
+ import android.app.ondeviceintelligence.IListFeaturesCallback;
+ import android.app.ondeviceintelligence.IFeatureCallback;
+ import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+ import android.app.ondeviceintelligence.IResponseCallback;
+ import android.app.ondeviceintelligence.IStreamingResponseCallback;
+ import android.app.ondeviceintelligence.IProcessingSignal;
+ import android.app.ondeviceintelligence.ITokenInfoCallback;
+
+
+ /**
+ * Interface for a OnDeviceIntelligenceManager for managing OnDeviceIntelligenceService and OnDeviceSandboxedInferenceService.
+ *
+ * @hide
+ */
+interface IOnDeviceIntelligenceManager {
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getVersion(in RemoteCallback remoteCallback) = 1;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeature(in int featureId, in IFeatureCallback remoteCallback) = 2;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void listFeatures(in IListFeaturesCallback listFeaturesCallback) = 3;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback) = 4;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestFeatureDownload(in Feature feature, in AndroidFuture cancellationSignalFuture, in IDownloadCallback callback) = 5;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestTokenInfo(in Feature feature, in Bundle requestBundle, in AndroidFuture cancellationSignalFuture,
+ in ITokenInfoCallback tokenInfocallback) = 6;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequest(in Feature feature, in Bundle requestBundle, int requestType,
+ in AndroidFuture cancellationSignalFuture,
+ in AndroidFuture processingSignalFuture,
+ in IResponseCallback responseCallback) = 7;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequestStreaming(in Feature feature,
+ in Bundle requestBundle, int requestType, in AndroidFuture cancellationSignalFuture,
+ in AndroidFuture processingSignalFuture,
+ in IStreamingResponseCallback streamingCallback) = 8;
+
+ String getRemoteServicePackageName() = 9;
+
+ List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) = 10;
+ }
diff --git a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
index 03946eebd40b..03946eebd40b 100644
--- a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
new file mode 100644
index 000000000000..6f07693dd39c
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
@@ -0,0 +1,24 @@
+/*
+* Copyright 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 android.app.ondeviceintelligence;
+
+import android.os.Bundle;
+
+/* @hide */
+oneway interface IRemoteCallback {
+ void sendResult(in Bundle data);
+}
diff --git a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IResponseCallback.aidl
index 270b600e2de5..270b600e2de5 100644
--- a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IResponseCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
index 3e902405f3e0..3e902405f3e0 100644
--- a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
index 958bef0a93e0..958bef0a93e0 100644
--- a/core/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl
new file mode 100644
index 000000000000..6f6325408979
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+@JavaOnlyStableParcelable parcelable InferenceInfo;
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
index cae8db27a435..cae8db27a435 100644
--- a/core/java/android/app/ondeviceintelligence/InferenceInfo.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
index 03ff563a88c0..2881c9d217dc 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
@@ -20,13 +20,14 @@ package android.app.ondeviceintelligence;
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.PersistableBundle;
-import androidx.annotation.IntDef;
-
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
@@ -124,8 +125,9 @@ public class OnDeviceIntelligenceException extends Exception {
PROCESSING_ERROR_SERVICE_UNAVAILABLE,
ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
PROCESSING_UPDATE_STATUS_CONNECTION_FAILED
- }, open = true)
+ })
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ @Retention(RetentionPolicy.SOURCE)
@interface OnDeviceIntelligenceError {
}
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
new file mode 100644
index 000000000000..7d35dd7f2237
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for OnDeviceIntelligence service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceFrameworkInitializer {
+ private OnDeviceIntelligenceFrameworkInitializer() {
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers
+ * OnDeviceIntelligence service to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides {@link
+ * SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(Context.ON_DEVICE_INTELLIGENCE_SERVICE,
+ OnDeviceIntelligenceManager.class,
+ (context, serviceBinder) -> {
+ IOnDeviceIntelligenceManager manager =
+ IOnDeviceIntelligenceManager.Stub.asInterface(serviceBinder);
+ return new OnDeviceIntelligenceManager(context, manager);
+ });
+ }
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
new file mode 100644
index 000000000000..dc0665a5cea7
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -0,0 +1,677 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.ondeviceintelligence.utils.BinderUtils;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.modules.utils.AndroidFuture;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.LongConsumer;
+
+/**
+ * Allows granted apps to manage on-device intelligence service configured on the device. Typical
+ * calling pattern will be to query and setup a required feature before proceeding to request
+ * processing.
+ *
+ * The contracts in this Manager class are designed to be open-ended in general, to allow
+ * interoperability. Therefore, it is recommended that implementations of this system-service
+ * expose this API to the clients via a separate sdk or library which has more defined contract.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class OnDeviceIntelligenceManager {
+ /**
+ * @hide
+ */
+ public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+
+ /**
+ * @hide
+ */
+ public static final String AUGMENT_REQUEST_CONTENT_BUNDLE_KEY =
+ "AugmentRequestContentBundleKey";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote {@link
+ * android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no requests in
+ * the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+ "on_device_intelligence_unbind_timeout_ms";
+
+ /**
+ * Timeout that represents maximum idle time before which a callback should be populated.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+ "on_device_intelligence_idle_timeout_ms";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote {@link
+ * android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there are no
+ * requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+ "on_device_inference_unbind_timeout_ms";
+
+ private static final String TAG = "OnDeviceIntelligence";
+ private final Context mContext;
+ private final IOnDeviceIntelligenceManager mService;
+
+
+ /**
+ * @hide
+ */
+ public OnDeviceIntelligenceManager(Context context, IOnDeviceIntelligenceManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Asynchronously get the version of the underlying remote implementation.
+ *
+ * @param versionConsumer consumer to populate the version of remote implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getVersion(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull LongConsumer versionConsumer) {
+ try {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ if (result == null) {
+ BinderUtils.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
+ }
+ long version = result.getLong(API_VERSION_BUNDLE_KEY);
+ BinderUtils.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
+ });
+ mService.getVersion(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Get package name configured for providing the remote implementation for this system service.
+ */
+ @Nullable
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public String getRemoteServicePackageName() {
+ String result;
+ try {
+ result = mService.getRemoteServicePackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return result;
+ }
+
+ /**
+ * Asynchronously get feature for a given id.
+ *
+ * @param featureId the identifier pointing to the feature.
+ * @param featureReceiver callback to populate the feature object for given identifier.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeature(
+ int featureId,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Feature, OnDeviceIntelligenceException> featureReceiver) {
+ try {
+ IFeatureCallback callback =
+ new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.getFeature(featureId, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously get a list of features that are supported for the caller.
+ *
+ * @param featureListReceiver callback to populate the list of features.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void listFeatures(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<List<Feature>, OnDeviceIntelligenceException> featureListReceiver) {
+ try {
+ IListFeaturesCallback callback =
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.listFeatures(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method should be used to fetch details about a feature which need some additional
+ * computation, that can be inefficient to return in all calls to {@link #getFeature}. Callers
+ * and implementation can utilize the {@link Feature#getFeatureParams()} to pass hint on what
+ * details are expected by the caller.
+ *
+ * @param feature the feature to check status for.
+ * @param featureDetailsReceiver callback to populate the feature details to.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeatureDetails(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> featureDetailsReceiver) {
+ try {
+ IFeatureDetailsCallback callback = new IFeatureDetailsCallback.Stub() {
+
+ @Override
+ public void onSuccess(FeatureDetails result) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onError(
+ new OnDeviceIntelligenceException(errorCode,
+ errorMessage, errorParams))));
+ }
+ };
+ mService.getFeatureDetails(feature, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method handles downloading all model and config files required to process requests
+ * sent against a given feature. The caller can listen to updates on the download status via
+ * the callback.
+ *
+ * Note: If a feature was already requested for downloaded previously, the onDownloadFailed
+ * callback would be invoked with {@link DownloadCallback#DOWNLOAD_FAILURE_STATUS_DOWNLOADING}.
+ * In such cases, clients should query the feature status via {@link #getFeatureDetails} to
+ * check on the feature's download status.
+ *
+ * @param feature feature to request download for.
+ * @param callback callback to populate updates about download status.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestFeatureDownload(@NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull DownloadCallback callback) {
+ try {
+ IDownloadCallback downloadCallback = new IDownloadCallback.Stub() {
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadStarted(bytesToDownload)));
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadProgress(bytesDownloaded)));
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadFailed(failureStatus, errorMessage,
+ errorParams)));
+ }
+
+ @Override
+ public void onDownloadCompleted(PersistableBundle downloadParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadCompleted(downloadParams)));
+ }
+ };
+
+ mService.requestFeatureDownload(feature,
+ configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+ downloadCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * The methods computes the token related information for a given request payload using the
+ * provided {@link Feature}.
+ *
+ * @param feature feature associated with the request.
+ * @param request request and associated params represented by the Bundle
+ * data.
+ * @param outcomeReceiver callback to populate the token info or exception in case of
+ * failure.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestTokenInfo(@NonNull Feature feature, @NonNull @InferenceParams Bundle request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<TokenInfo,
+ OnDeviceIntelligenceException> outcomeReceiver) {
+ try {
+ ITokenInfoCallback callback = new ITokenInfoCallback.Stub() {
+ @Override
+ public void onSuccess(TokenInfo tokenInfo) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onResult(tokenInfo)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+
+ mService.requestTokenInfo(feature, request,
+ configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+ callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Asynchronously Process a request based on the associated params, to populate a
+ * response in
+ * {@link OutcomeReceiver#onResult} callback or failure callback status code if there
+ * was a
+ * failure.
+ *
+ * @param feature feature associated with the request.
+ * @param request request and associated params represented by the Bundle
+ * data.
+ * @param requestType type of request being sent for processing the content.
+ * @param cancellationSignal signal to invoke cancellation.
+ * @param processingSignal signal to send custom signals in the
+ * remote implementation.
+ * @param callbackExecutor executor to run the callback on.
+ * @param processingCallback callback to populate the response content and
+ * associated params.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void processRequest(@NonNull Feature feature, @NonNull @InferenceParams Bundle request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull ProcessingCallback processingCallback) {
+ try {
+ IResponseCallback callback = new IResponseCallback.Stub() {
+ @Override
+ public void onSuccess(@InferenceParams Bundle result) {
+ BinderUtils.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(() -> processingCallback.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> processingCallback.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage, errorParams))));
+ }
+
+ @Override
+ public void onDataAugmentRequest(@NonNull @InferenceParams Bundle request,
+ @NonNull RemoteCallback contentCallback) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> processingCallback.onDataAugmentRequest(request, result -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY, result);
+ callbackExecutor.execute(() -> contentCallback.sendResult(bundle));
+ })));
+ }
+ };
+
+
+ mService.processRequest(feature, request, requestType,
+ configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+ configureRemoteProcessingSignalFuture(processingSignal, callbackExecutor),
+ callback);
+
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Variation of {@link #processRequest} that asynchronously processes a request in a
+ * streaming
+ * fashion, where new content is pushed to caller in chunks via the
+ * {@link StreamingProcessingCallback#onPartialResult}. After the streaming is complete,
+ * the service should call {@link StreamingProcessingCallback#onResult} and can optionally
+ * populate the complete the full response {@link Bundle} as part of the callback in cases
+ * when the final response contains an enhanced aggregation of the contents already
+ * streamed.
+ *
+ * @param feature feature associated with the request.
+ * @param request request and associated params represented by the Bundle
+ * data.
+ * @param requestType type of request being sent for processing the content.
+ * @param cancellationSignal signal to invoke cancellation.
+ * @param processingSignal signal to send custom signals in the
+ * remote implementation.
+ * @param streamingProcessingCallback streaming callback to populate the response content and
+ * associated params.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void processRequestStreaming(@NonNull Feature feature,
+ @NonNull @InferenceParams Bundle request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull StreamingProcessingCallback streamingProcessingCallback) {
+ try {
+ IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() {
+ @Override
+ public void onNewContent(@InferenceParams Bundle result) {
+ BinderUtils.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingProcessingCallback.onPartialResult(result));
+ });
+ }
+
+ @Override
+ public void onSuccess(@InferenceParams Bundle result) {
+ BinderUtils.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingProcessingCallback.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ BinderUtils.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingProcessingCallback.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage, errorParams)));
+ });
+ }
+
+
+ @Override
+ public void onDataAugmentRequest(@NonNull @InferenceParams Bundle content,
+ @NonNull RemoteCallback contentCallback) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> streamingProcessingCallback.onDataAugmentRequest(content,
+ contentResponse -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY,
+ contentResponse);
+ callbackExecutor.execute(
+ () -> contentCallback.sendResult(bundle));
+ })));
+ }
+ };
+
+ mService.processRequestStreaming(
+ feature, request, requestType,
+ configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+ configureRemoteProcessingSignalFuture(processingSignal, callbackExecutor),
+ callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This is primarily intended to be used to attribute/blame on-device intelligence power usage,
+ * via the configured remote implementation, to its actual caller.
+ *
+ * @param startTimeEpochMillis epoch millis used to filter the InferenceInfo events.
+ * @return InferenceInfo events since the passed in startTimeEpochMillis.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ @FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+ public @NonNull List<InferenceInfo> getLatestInferenceInfo(@CurrentTimeMillisLong long startTimeEpochMillis) {
+ try {
+ return mService.getLatestInferenceInfo(startTimeEpochMillis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /** Request inference with provided Bundle and Params. */
+ public static final int REQUEST_TYPE_INFERENCE = 0;
+
+ /**
+ * Prepares the remote implementation environment for e.g.loading inference runtime etc
+ * .which
+ * are time consuming beforehand to remove overhead and allow quick processing of requests
+ * thereof.
+ */
+ public static final int REQUEST_TYPE_PREPARE = 1;
+
+ /** Request Embeddings of the passed-in Bundle. */
+ public static final int REQUEST_TYPE_EMBEDDINGS = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ REQUEST_TYPE_INFERENCE,
+ REQUEST_TYPE_PREPARE,
+ REQUEST_TYPE_EMBEDDINGS
+ })
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER,
+ ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType {
+ }
+
+ /**
+ * {@link Bundle}s annotated with this type will be validated that they are in-effect read-only
+ * when passed via Binder IPC. Following restrictions apply :
+ * <ul>
+ * <li> {@link PersistableBundle}s are allowed.</li>
+ * <li> Any primitive types or their collections can be added as usual.</li>
+ * <li>IBinder objects should *not* be added.</li>
+ * <li>Parcelable data which has no active-objects, should be added as
+ * {@link Bundle#putByteArray}</li>
+ * <li>Parcelables have active-objects, only following types will be allowed</li>
+ * <ul>
+ * <li>{@link android.os.ParcelFileDescriptor} opened in
+ * {@link android.os.ParcelFileDescriptor#MODE_READ_ONLY}</li>
+ * </ul>
+ * </ul>
+ *
+ * In all other scenarios the system-server might throw a
+ * {@link android.os.BadParcelableException} if the Bundle validation fails.
+ *
+ * @hide
+ */
+ @Target({ElementType.PARAMETER, ElementType.FIELD})
+ public @interface StateParams {
+ }
+
+ /**
+ * This is an extension of {@link StateParams} but for purpose of inference few other types are
+ * also allowed as read-only, as listed below.
+ *
+ * <li>{@link Bitmap} set as immutable.</li>
+ * <li>{@link android.database.CursorWindow}</li>
+ * <li>{@link android.os.SharedMemory} set to {@link OsConstants#PROT_READ}</li>
+ * </ul>
+ * </ul>
+ *
+ * In all other scenarios the system-server might throw a
+ * {@link android.os.BadParcelableException} if the Bundle validation fails.
+ *
+ * @hide
+ */
+ @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE_USE})
+ public @interface InferenceParams {
+ }
+
+ /**
+ * This is an extension of {@link StateParams} with the exception that it allows writing
+ * {@link Bitmap} as part of the response.
+ *
+ * In all other scenarios the system-server might throw a
+ * {@link android.os.BadParcelableException} if the Bundle validation fails.
+ *
+ * @hide
+ */
+ @Target({ElementType.PARAMETER, ElementType.FIELD})
+ public @interface ResponseParams {
+ }
+
+ @Nullable
+ private static AndroidFuture<IBinder> configureRemoteCancellationFuture(
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor callbackExecutor) {
+ if (cancellationSignal == null) {
+ return null;
+ }
+ AndroidFuture<IBinder> cancellationFuture = new AndroidFuture<>();
+ cancellationFuture.whenCompleteAsync(
+ (cancellationTransport, error) -> {
+ if (error != null || cancellationTransport == null) {
+ Log.e(TAG, "Unable to receive the remote cancellation signal.", error);
+ } else {
+ ICancellationSignal remoteCancellationSignal =
+ ICancellationSignal.Stub.asInterface(cancellationTransport);
+ cancellationSignal.setOnCancelListener(
+ () -> {
+ try {
+ remoteCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to propagate cancellation signal.",
+ e);
+ }
+ });
+ }
+ }, callbackExecutor);
+ return cancellationFuture;
+ }
+
+ @Nullable
+ private static AndroidFuture<IBinder> configureRemoteProcessingSignalFuture(
+ ProcessingSignal processingSignal, Executor executor) {
+ if (processingSignal == null) {
+ return null;
+ }
+ AndroidFuture<IBinder> processingSignalFuture = new AndroidFuture<>();
+ processingSignalFuture.whenCompleteAsync(
+ (transport, error) -> {
+ if (error != null || transport == null) {
+ Log.e(TAG, "Unable to receive the remote processing signal.", error);
+ } else {
+ processingSignal.setRemote(IProcessingSignal.Stub.asInterface(transport));
+ }
+ }, executor);
+ return processingSignalFuture;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingCallback.java
index e50d6b1fa97a..e50d6b1fa97a 100644
--- a/core/java/android/app/ondeviceintelligence/ProcessingCallback.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingCallback.java
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingSignal.java
index 733f4fad96f4..733f4fad96f4 100644
--- a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingSignal.java
diff --git a/core/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
index 7ee2af7376ed..7ee2af7376ed 100644
--- a/core/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl
new file mode 100644
index 000000000000..599b337fd20f
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+@JavaOnlyStableParcelable parcelable TokenInfo;
diff --git a/core/java/android/app/ondeviceintelligence/TokenInfo.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.java
index 035cc4b365b5..035cc4b365b5 100644
--- a/core/java/android/app/ondeviceintelligence/TokenInfo.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.java
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java
new file mode 100644
index 000000000000..2916f030e3d0
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.app.ondeviceintelligence.utils;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import java.util.function.Supplier;
+
+/**
+ * Collection of utilities for {@link Binder} and related classes.
+ * @hide
+ */
+public class BinderUtils {
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
+ *
+ * Any exception thrown by the given action will be caught and rethrown after the call to
+ * {@link Binder#restoreCallingIdentity}
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T extends Exception> void withCleanCallingIdentity(
+ @NonNull ThrowingRunnable<T> action) throws T {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ action.run();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * Like a Runnable, but declared to throw an exception.
+ *
+ * @param <T> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingRunnable<T extends Exception> {
+ /** @see java.lang.Runnable */
+ void run() throws T;
+ }
+
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} returning the
+ * result.
+ *
+ * <p>Any exception thrown by the given action will be caught and rethrown after
+ * the call to {@link Binder#restoreCallingIdentity}.
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T, E extends Exception> T withCleanCallingIdentity(
+ @NonNull ThrowingSupplier<T, E> action) throws E {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return action.get();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * An equivalent of {@link Supplier}
+ *
+ * @param <T> The class which is declared to be returned.
+ * @param <E> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingSupplier<T, E extends Exception> {
+ /** @see java.util.function.Supplier */
+ T get() throws E;
+ }
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
new file mode 100644
index 000000000000..cba18c1ef36d
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.ParcelFileDescriptor;
+import android.app.ondeviceintelligence.ICancellationSignal;
+import android.os.RemoteCallback;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import com.android.modules.utils.AndroidFuture;
+import android.service.ondeviceintelligence.IRemoteProcessingService;
+
+
+/**
+ * Interface for a concrete implementation to provide on device intelligence services.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceIntelligenceService {
+ void getVersion(in RemoteCallback remoteCallback);
+ void getFeature(int callerUid, int featureId, in IFeatureCallback featureCallback);
+ void listFeatures(int callerUid, in IListFeaturesCallback listFeaturesCallback);
+ void getFeatureDetails(int callerUid, in Feature feature, in IFeatureDetailsCallback featureDetailsCallback);
+ void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+ void requestFeatureDownload(int callerUid, in Feature feature,
+ in AndroidFuture cancellationSignal,
+ in IDownloadCallback downloadCallback);
+ void registerRemoteServices(in IRemoteProcessingService remoteProcessingService);
+ void notifyInferenceServiceConnected();
+ void notifyInferenceServiceDisconnected();
+ void ready();
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
new file mode 100644
index 000000000000..504fdd9b17f9
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IRemoteCallback;
+import android.app.ondeviceintelligence.ICancellationSignal;
+import android.os.PersistableBundle;
+import android.os.Bundle;
+import com.android.modules.utils.AndroidFuture;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
+
+/**
+ * Interface for a concrete implementation to provide on-device sandboxed inference.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceSandboxedInferenceService {
+ void registerRemoteStorageService(in IRemoteStorageService storageService,
+ in IRemoteCallback remoteCallback) = 0;
+ void requestTokenInfo(int callerUid, in Feature feature, in Bundle request,
+ in AndroidFuture cancellationSignal,
+ in ITokenInfoCallback tokenInfoCallback) = 1;
+ void processRequest(int callerUid, in Feature feature, in Bundle request, in int requestType,
+ in AndroidFuture cancellationSignal,
+ in AndroidFuture processingSignal,
+ in IResponseCallback callback) = 2;
+ void processRequestStreaming(int callerUid, in Feature feature, in Bundle request, in int requestType,
+ in AndroidFuture cancellationSignal,
+ in AndroidFuture processingSignal,
+ in IStreamingResponseCallback callback) = 3;
+ void updateProcessingState(in Bundle processingState,
+ in IProcessingUpdateStatusCallback callback) = 4;
+} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
index 7ead8690abb4..7ead8690abb4 100644
--- a/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
index 32a8a6a70406..32a8a6a70406 100644
--- a/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
diff --git a/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
new file mode 100644
index 000000000000..253df890b198
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+import com.android.modules.utils.AndroidFuture;
+
+/**
+ * Interface for a concrete implementation to provide access to storage read access
+ * for the isolated process.
+ *
+ * @hide
+ */
+oneway interface IRemoteStorageService {
+ void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
new file mode 100644
index 000000000000..6907e2bdf2b3
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -0,0 +1,676 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.ICancellationSignal;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.utils.BinderUtils;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.modules.utils.AndroidFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Abstract base class for performing setup for on-device inference and providing file access to
+ * the isolated counter part {@link OnDeviceSandboxedInferenceService}.
+ *
+ * <p> A service that provides configuration and model files relevant to performing inference on
+ * device. The system's default OnDeviceIntelligenceService implementation is configured in
+ * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleOnDeviceIntelligenceService"
+ * android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceIntelligenceService extends Service {
+ private static final int MSG_ON_READY = 1;
+ private static final int MSG_GET_VERSION = 2;
+ private static final int MSG_LIST_FEATURES = 3;
+ private static final int MSG_GET_FEATURE = 4;
+ private static final int MSG_GET_FEATURE_DETAILS = 5;
+ private static final int MSG_DOWNLOAD_FEATURE = 6;
+ private static final int MSG_GET_READ_ONLY_FILE_DESCRIPTOR = 7;
+ private static final int MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP = 8;
+ private static final int MSG_REGISTER_REMOTE_SERVICES = 9;
+ private static final int MSG_INFERENCE_SERVICE_CONNECTED = 10;
+ private static final int MSG_INFERENCE_SERVICE_DISCONNECTED = 11;
+
+ private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+
+ private volatile IRemoteProcessingService mRemoteProcessingService;
+ private Handler mHandler;
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MSG_ON_READY:
+ OnDeviceIntelligenceService.this.onReady();
+ break;
+ case MSG_GET_VERSION:
+ OnDeviceIntelligenceService.this.onGetVersion(
+ (LongConsumer) msg.obj);
+ break;
+ case MSG_LIST_FEATURES:
+ OnDeviceIntelligenceService.this.onListFeatures(
+ msg.arg1,
+ (OutcomeReceiver<List<Feature>, OnDeviceIntelligenceException>) msg.obj);
+ break;
+ case MSG_GET_FEATURE:
+ GetFeatureParams params = (GetFeatureParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetFeature(
+ msg.arg1,
+ msg.arg2,
+ params.callback);
+ break;
+ case MSG_GET_FEATURE_DETAILS:
+ FeatureDetailsParams detailsParams = (FeatureDetailsParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetFeatureDetails(
+ msg.arg1,
+ detailsParams.feature,
+ detailsParams.callback);
+ break;
+ case MSG_DOWNLOAD_FEATURE:
+ DownloadParams downloadParams = (DownloadParams) msg.obj;
+ OnDeviceIntelligenceService.this.onDownloadFeature(
+ msg.arg1,
+ downloadParams.feature,
+ downloadParams.cancellationSignal,
+ downloadParams.callback);
+ break;
+ case MSG_GET_READ_ONLY_FILE_DESCRIPTOR:
+ FileDescriptorParams fdParams = (FileDescriptorParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(
+ fdParams.fileName,
+ fdParams.future);
+ break;
+ case MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP:
+ FeatureFileDescriptorParams ffdParams =
+ (FeatureFileDescriptorParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
+ ffdParams.feature,
+ ffdParams.consumer);
+ break;
+ case MSG_REGISTER_REMOTE_SERVICES:
+ mRemoteProcessingService = (IRemoteProcessingService) msg.obj;
+ break;
+ case MSG_INFERENCE_SERVICE_CONNECTED:
+ OnDeviceIntelligenceService.this.onInferenceServiceConnected();
+ break;
+ case MSG_INFERENCE_SERVICE_DISCONNECTED:
+ OnDeviceIntelligenceService.this.onInferenceServiceDisconnected();
+ break;
+ }
+ }
+ };
+ }
+
+
+ /**
+ * @hide
+ */
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceIntelligenceService.Stub() {
+ /** {@inheritDoc} */
+ @Override
+ public void ready() {
+ mHandler.sendEmptyMessage(MSG_ON_READY);
+ }
+
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) {
+ Objects.requireNonNull(remoteCallback);
+ Message msg = Message.obtain(mHandler, MSG_GET_VERSION,
+ (LongConsumer) (l -> {
+ Bundle b = new Bundle();
+ b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
+ remoteCallback.sendResult(b);
+ }));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void listFeatures(int callerUid,
+ IListFeaturesCallback listFeaturesCallback) {
+ Objects.requireNonNull(listFeaturesCallback);
+ Message msg = Message.obtain(mHandler, MSG_LIST_FEATURES,
+ callerUid, 0, wrapListFeaturesCallback(listFeaturesCallback));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void getFeature(int callerUid, int id, IFeatureCallback featureCallback) {
+ Objects.requireNonNull(featureCallback);
+ Message msg = Message.obtain(mHandler, MSG_GET_FEATURE,
+ callerUid, id,
+ new GetFeatureParams(wrapFeatureCallback(featureCallback)));
+ mHandler.sendMessage(msg);
+ }
+
+
+ @Override
+ public void getFeatureDetails(int callerUid, Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+ Message msg = Message.obtain(mHandler, MSG_GET_FEATURE_DETAILS,
+ new FeatureDetailsParams(feature,
+ wrapFeatureDetailsCallback(featureDetailsCallback)));
+ msg.arg1 = callerUid;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void requestFeatureDownload(int callerUid, Feature feature,
+ AndroidFuture cancellationSignalFuture,
+ IDownloadCallback downloadCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ if (cancellationSignalFuture != null) {
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
+ cancellationSignalFuture.complete(transport);
+ }
+
+ Message msg = Message.obtain(mHandler, MSG_DOWNLOAD_FEATURE,
+ new DownloadParams(feature,
+ cancellationSignalFuture != null ? cancellationSignal : null,
+ wrapDownloadCallback(downloadCallback)));
+ msg.arg1 = callerUid;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void getReadOnlyFileDescriptor(String fileName,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ Objects.requireNonNull(fileName);
+ Objects.requireNonNull(future);
+ Message msg = Message.obtain(mHandler, MSG_GET_READ_ONLY_FILE_DESCRIPTOR,
+ new FileDescriptorParams(fileName, future));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature, RemoteCallback remoteCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(remoteCallback);
+ Message msg = Message.obtain(mHandler,
+ MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP,
+ new FeatureFileDescriptorParams(feature, parcelFileDescriptorMap -> {
+ Bundle bundle = new Bundle();
+ parcelFileDescriptorMap.forEach(bundle::putParcelable);
+ remoteCallback.sendResult(bundle);
+ tryClosePfds(parcelFileDescriptorMap.values());
+ }));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void registerRemoteServices(
+ IRemoteProcessingService remoteProcessingService) {
+ mRemoteProcessingService = remoteProcessingService;
+ }
+
+ @Override
+ public void notifyInferenceServiceConnected() {
+ mHandler.sendEmptyMessage(MSG_INFERENCE_SERVICE_CONNECTED);
+ }
+
+ @Override
+ public void notifyInferenceServiceDisconnected() {
+ mHandler.sendEmptyMessage(MSG_INFERENCE_SERVICE_DISCONNECTED);
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+ // Parameter holder classes
+ private static class GetFeatureParams {
+ final OutcomeReceiver<Feature, OnDeviceIntelligenceException> callback;
+
+ GetFeatureParams(OutcomeReceiver<Feature, OnDeviceIntelligenceException> callback) {
+ this.callback = callback;
+ }
+ }
+
+ private static class FeatureDetailsParams {
+ final Feature feature;
+ final OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> callback;
+
+ FeatureDetailsParams(Feature feature,
+ OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> callback) {
+ this.feature = feature;
+ this.callback = callback;
+ }
+ }
+
+ private static class DownloadParams {
+ final Feature feature;
+ final CancellationSignal cancellationSignal;
+ final DownloadCallback callback;
+
+ DownloadParams(Feature feature, CancellationSignal cancellationSignal,
+ DownloadCallback callback) {
+ this.feature = feature;
+ this.cancellationSignal = cancellationSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class FileDescriptorParams {
+ final String fileName;
+ final AndroidFuture<ParcelFileDescriptor> future;
+
+ FileDescriptorParams(String fileName, AndroidFuture<ParcelFileDescriptor> future) {
+ this.fileName = fileName;
+ this.future = future;
+ }
+ }
+
+ private static class FeatureFileDescriptorParams {
+ final Feature feature;
+ final Consumer<Map<String, ParcelFileDescriptor>> consumer;
+
+ FeatureFileDescriptorParams(Feature feature,
+ Consumer<Map<String, ParcelFileDescriptor>> consumer) {
+ this.feature = feature;
+ this.consumer = consumer;
+ }
+ }
+
+ /**
+ * Using this signal to assertively a signal each time service binds successfully, used only in
+ * tests to get a signal that service instance is ready. This is needed because we cannot rely
+ * on {@link #onCreate} or {@link #onBind} to be invoke on each binding.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onReady() {
+ }
+
+
+ /**
+ * Invoked when a new instance of the remote inference service is created.
+ * This method should be used as a signal to perform any initialization operations, for e.g. by
+ * invoking the {@link #updateProcessingState} method to initialize the remote processing
+ * service.
+ */
+ public abstract void onInferenceServiceConnected();
+
+
+ /**
+ * Invoked when an instance of the remote inference service is disconnected.
+ */
+ public abstract void onInferenceServiceDisconnected();
+
+
+ /**
+ * Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference
+ * service if there is a state change to be performed. State change could be config updates,
+ * performing initialization or cleanup tasks in the remote inference service.
+ * The Bundle passed in here is expected to be read-only and will be rejected if it has any
+ * writable fields as detailed under {@link StateParams}.
+ *
+ * @param processingState the updated state to be applied.
+ * @param callbackExecutor executor to the run status callback on.
+ * @param statusReceiver receiver to get status of the update state operation.
+ */
+ public final void updateProcessingState(@NonNull @StateParams Bundle processingState,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> statusReceiver) {
+ Objects.requireNonNull(callbackExecutor);
+ if (mRemoteProcessingService == null) {
+ throw new IllegalStateException("Remote processing service is unavailable.");
+ }
+ try {
+ mRemoteProcessingService.updateProcessingState(processingState,
+ new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle result) {
+ BinderUtils.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> statusReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> statusReceiver.onError(
+ new OnDeviceIntelligenceException(
+ errorCode, errorMessage))));
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error in updateProcessingState: " + e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private OutcomeReceiver<Feature,
+ OnDeviceIntelligenceException> wrapFeatureCallback(
+ IFeatureCallback featureCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull Feature feature) {
+ try {
+ featureCallback.onSuccess(feature);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceException exception) {
+ try {
+ featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceException> wrapListFeaturesCallback(
+ IListFeaturesCallback listFeaturesCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull List<Feature> features) {
+ try {
+ listFeaturesCallback.onSuccess(features);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceException exception) {
+ try {
+ listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceException> wrapFeatureDetailsCallback(
+ IFeatureDetailsCallback featureStatusCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(FeatureDetails result) {
+ try {
+ featureStatusCallback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceException exception) {
+ try {
+ featureStatusCallback.onFailure(exception.getErrorCode(),
+ exception.getMessage(), exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+ };
+ }
+
+
+ private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) {
+ return new DownloadCallback() {
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ try {
+ downloadCallback.onDownloadStarted(bytesToDownload);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, @NonNull PersistableBundle errorParams) {
+ try {
+ downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadProgress(long totalBytesDownloaded) {
+ try {
+ downloadCallback.onDownloadProgress(totalBytesDownloaded);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) {
+ try {
+ downloadCallback.onDownloadCompleted(persistableBundle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+ };
+ }
+
+ private static void tryClosePfds(Collection<ParcelFileDescriptor> pfds) {
+ pfds.forEach(pfd -> {
+ try {
+ pfd.close();
+ } catch (Exception e) {
+ Log.w(TAG, "Error closing FD", e);
+ }
+ });
+ }
+
+ private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
+ @NonNull AndroidFuture<ParcelFileDescriptor> future) {
+ Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
+ BinderUtils.withCleanCallingIdentity(() -> {
+ Slog.v(TAG,
+ "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
+ File f = new File(getBaseContext().getFilesDir(), fileName);
+ if (!f.exists()) {
+ f = new File(fileName);
+ }
+ ParcelFileDescriptor pfd = null;
+ try {
+ pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+ future.completeExceptionally(e);
+ } finally {
+ future.complete(pfd);
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Error closing FD", e);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Provide implementation for a scenario when caller wants to get all feature related
+ * file-descriptors that might be required for processing a request for the corresponding the
+ * feature.
+ *
+ * @param feature the feature for which files need to be opened.
+ * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and
+ * corresponding ParcelDescriptor to be used in a remote
+ * service.
+ */
+ public abstract void onGetReadOnlyFeatureFileDescriptorMap(
+ @NonNull Feature feature,
+ @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer);
+
+ /**
+ * Request download for feature that is requested and listen to download progress updates. If
+ * the download completes successfully, success callback should be populated.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param feature the feature for which files need to be downlaoded.
+ * process.
+ * @param cancellationSignal signal to attach a listener to, and receive cancellation signals
+ * from thw client.
+ * @param downloadCallback callback to populate download updates for clients to listen on..
+ */
+ public abstract void onDownloadFeature(
+ int callerUid, @NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull DownloadCallback downloadCallback);
+
+ /**
+ * Provide feature details for the passed in feature. Usually the client and remote
+ * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what
+ * details the client is looking for.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param feature the feature for which status needs to be known.
+ * @param featureDetailsCallback callback to populate the resulting feature status.
+ */
+ public abstract void onGetFeatureDetails(int callerUid, @NonNull Feature feature,
+ @NonNull OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceException> featureDetailsCallback);
+
+
+ /**
+ * Get feature using the provided identifier to the remote implementation.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param featureCallback callback to populate the features list.
+ */
+ public abstract void onGetFeature(int callerUid, int featureId,
+ @NonNull OutcomeReceiver<Feature,
+ OnDeviceIntelligenceException> featureCallback);
+
+ /**
+ * List all features which are available in the remote implementation. The implementation might
+ * choose to provide only a certain list of features based on the caller.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param listFeaturesCallback callback to populate the features list.
+ */
+ public abstract void onListFeatures(int callerUid, @NonNull OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceException> listFeaturesCallback);
+
+ /**
+ * Provides a long value representing the version of the remote implementation processing
+ * requests.
+ *
+ * @param versionConsumer consumer to populate the version.
+ */
+ public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+}
diff --git a/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
new file mode 100644
index 000000000000..315dbaf919e5
--- /dev/null
+++ b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -0,0 +1,734 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.AUGMENT_REQUEST_CONTENT_BUNDLE_KEY;
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.ICancellationSignal;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IRemoteCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.InferenceParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.ProcessingCallback;
+import android.app.ondeviceintelligence.ProcessingSignal;
+import android.app.ondeviceintelligence.StreamingProcessingCallback;
+import android.app.ondeviceintelligence.TokenInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.modules.utils.AndroidFuture;
+import com.android.modules.utils.HandlerExecutor;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for performing inference in a isolated process. This service exposes its
+ * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * <p> A service that provides methods to perform on-device inference both in streaming and
+ * non-streaming fashion. Also, provides a way to register a storage service that will be used to
+ * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
+ *
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleSandboxedInferenceService"
+ * android:permission="android.permission.BIND_ONDEVICE_SANDBOXED_INFERENCE_SERVICE"
+ * android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceSandboxedInferenceService extends Service {
+ private static final String TAG = OnDeviceSandboxedInferenceService.class.getSimpleName();
+
+ private static final int MSG_TOKEN_INFO_REQUEST = 1;
+ private static final int MSG_PROCESS_REQUEST_STREAMING = 2;
+ private static final int MSG_PROCESS_REQUEST = 3;
+ private static final int MSG_UPDATE_PROCESSING_STATE = 4;
+
+
+ /**
+ * @hide
+ */
+ public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
+
+ // TODO(339594686): make API
+ /**
+ * @hide
+ */
+ public static final String REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY =
+ "register_model_update_callback";
+ /**
+ * @hide
+ */
+ public static final String MODEL_LOADED_BUNDLE_KEY = "model_loaded";
+ /**
+ * @hide
+ */
+ public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+ /**
+ * @hide
+ */
+ public static final String MODEL_LOADED_BROADCAST_INTENT =
+ "android.service.ondeviceintelligence.MODEL_LOADED";
+ /**
+ * @hide
+ */
+ public static final String MODEL_UNLOADED_BROADCAST_INTENT =
+ "android.service.ondeviceintelligence.MODEL_UNLOADED";
+
+ /**
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_UPDATE_BUNDLE_KEY = "device_config_update";
+
+ private IRemoteStorageService mRemoteStorageService;
+ private Handler mHandler;
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TOKEN_INFO_REQUEST:
+ TokenInfoParams params = (TokenInfoParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onTokenInfoRequest(
+ msg.arg1,
+ params.feature,
+ params.request,
+ params.cancellationSignal,
+ params.callback);
+ break;
+ case MSG_PROCESS_REQUEST_STREAMING:
+ StreamingRequestParams streamParams = (StreamingRequestParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onProcessRequestStreaming(
+ msg.arg1,
+ streamParams.feature,
+ streamParams.request,
+ msg.arg2,
+ streamParams.cancellationSignal,
+ streamParams.processingSignal,
+ streamParams.callback);
+ break;
+ case MSG_PROCESS_REQUEST:
+ RequestParams requestParams = (RequestParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onProcessRequest(
+ msg.arg1,
+ requestParams.feature,
+ requestParams.request,
+ msg.arg2,
+ requestParams.cancellationSignal,
+ requestParams.processingSignal,
+ requestParams.callback);
+ break;
+ case MSG_UPDATE_PROCESSING_STATE:
+ UpdateStateParams stateParams = (UpdateStateParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onUpdateProcessingState(
+ stateParams.processingState,
+ stateParams.callback);
+ break;
+ }
+ }
+ };
+ }
+
+ // Parameter holder classes
+ private static class TokenInfoParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback;
+
+ TokenInfoParams(Feature feature, Bundle request, CancellationSignal cancellationSignal,
+ OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class StreamingRequestParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final ProcessingSignal processingSignal;
+ final StreamingProcessingCallback callback;
+
+ StreamingRequestParams(Feature feature, Bundle request,
+ CancellationSignal cancellationSignal, ProcessingSignal processingSignal,
+ StreamingProcessingCallback callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.processingSignal = processingSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class RequestParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final ProcessingSignal processingSignal;
+ final ProcessingCallback callback;
+
+ RequestParams(Feature feature, Bundle request,
+ CancellationSignal cancellationSignal, ProcessingSignal processingSignal,
+ ProcessingCallback callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.processingSignal = processingSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class UpdateStateParams {
+ final Bundle processingState;
+ final OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> callback;
+
+ UpdateStateParams(Bundle processingState,
+ OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> callback) {
+ this.processingState = processingState;
+ this.callback = callback;
+ }
+ }
+
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceSandboxedInferenceService.Stub() {
+ @Override
+ public void registerRemoteStorageService(IRemoteStorageService storageService,
+ IRemoteCallback remoteCallback) throws RemoteException {
+ Objects.requireNonNull(storageService);
+ mRemoteStorageService = storageService;
+ remoteCallback.sendResult(Bundle.EMPTY);
+ }
+
+ @Override
+ public void requestTokenInfo(int callerUid, Feature feature, Bundle request,
+ AndroidFuture cancellationSignalFuture,
+ ITokenInfoCallback tokenInfoCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(tokenInfoCallback);
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ if (cancellationSignalFuture != null) {
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
+ cancellationSignalFuture.complete(transport);
+ }
+
+ Message msg = Message.obtain(mHandler, MSG_TOKEN_INFO_REQUEST,
+ callerUid, 0,
+ new TokenInfoParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
+ wrapTokenInfoCallback(tokenInfoCallback)));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void processRequestStreaming(int callerUid, Feature feature,
+ Bundle request, int requestType,
+ AndroidFuture cancellationSignalFuture,
+ AndroidFuture processingSignalFuture,
+ IStreamingResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(callback);
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ if (cancellationSignalFuture != null) {
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
+ cancellationSignalFuture.complete(transport);
+ }
+ IProcessingSignal processingSignalTransport = null;
+ if (processingSignalFuture != null) {
+ processingSignalTransport = ProcessingSignal.createTransport();
+ processingSignalFuture.complete(processingSignalTransport);
+ }
+
+ Message msg = Message.obtain(mHandler, MSG_PROCESS_REQUEST_STREAMING,
+ callerUid, requestType,
+ new StreamingRequestParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
+ ProcessingSignal.fromTransport(processingSignalTransport),
+ wrapStreamingResponseCallback(callback)));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void processRequest(int callerUid, Feature feature,
+ Bundle request, int requestType,
+ AndroidFuture cancellationSignalFuture,
+ AndroidFuture processingSignalFuture,
+ IResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(callback);
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ if (cancellationSignalFuture != null) {
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
+ cancellationSignalFuture.complete(transport);
+ }
+ IProcessingSignal processingSignalTransport = null;
+ if (processingSignalFuture != null) {
+ processingSignalTransport = ProcessingSignal.createTransport();
+ processingSignalFuture.complete(processingSignalTransport);
+ }
+
+ Message msg = Message.obtain(mHandler, MSG_PROCESS_REQUEST,
+ callerUid, requestType,
+ new RequestParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
+ ProcessingSignal.fromTransport(processingSignalTransport),
+ wrapResponseCallback(callback)));
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void updateProcessingState(Bundle processingState,
+ IProcessingUpdateStatusCallback callback) {
+ Objects.requireNonNull(processingState);
+ Objects.requireNonNull(callback);
+
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_PROCESSING_STATE,
+ new UpdateStateParams(processingState,
+ wrapOutcomeReceiver(callback)));
+ mHandler.sendMessage(msg);
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ /**
+ * Invoked when caller wants to obtain token info related to the payload in the passed
+ * content, associated with the provided feature.
+ * The expectation from the implementation is that when processing is complete, it
+ * should provide the token info in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param callback callback to populate failure or the token info for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onTokenInfoRequest(
+ int callerUid, @NonNull Feature feature,
+ @NonNull @InferenceParams Bundle request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in a
+ * streaming manner. The expectation from the implementation is that when processing the
+ * request,
+ * it periodically populates the {@link StreamingProcessingCallback#onPartialResult} to
+ * continuously
+ * provide partial Bundle results for the caller to utilize. Optionally the implementation can
+ * provide the complete response in the {@link StreamingProcessingCallback#onResult} upon
+ * processing completion.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate the partial responses, failure and optionally
+ * full response for the provided request.
+ */
+ @NonNull
+ public abstract void onProcessRequestStreaming(
+ int callerUid, @NonNull Feature feature,
+ @NonNull @InferenceParams Bundle request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull StreamingProcessingCallback callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in one shot
+ * completely.
+ * The expectation from the implementation is that when processing the request is complete, it
+ * should
+ * provide the complete response in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param callerUid UID of the caller that initiated this call chain.
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate failure and full response for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onProcessRequest(
+ int callerUid, @NonNull Feature feature,
+ @NonNull @InferenceParams Bundle request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull ProcessingCallback callback);
+
+
+ /**
+ * Invoked when processing environment needs to be updated or refreshed with fresh
+ * configuration, files or state.
+ *
+ * @param processingState contains updated state and params that are to be applied to the
+ * processing environmment,
+ * @param callback callback to populate the update status and if there are params
+ * associated with the status.
+ */
+ public abstract void onUpdateProcessingState(@NonNull @StateParams Bundle processingState,
+ @NonNull OutcomeReceiver<PersistableBundle,
+ OnDeviceIntelligenceException> callback);
+
+
+ /**
+ * Overrides {@link Context#openFileInput} to read files with the given file names under the
+ * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
+ * {@link Context#getFilesDir()} can be opened.
+ */
+ @Override
+ public final FileInputStream openFileInput(@NonNull String filename) throws
+ FileNotFoundException {
+ try {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ mRemoteStorageService.getReadOnlyFileDescriptor(filename, future);
+ ParcelFileDescriptor pfd = future.get();
+ return new FileInputStream(pfd.getFileDescriptor());
+ } catch (RemoteException | ExecutionException | InterruptedException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
+ /**
+ * Provides read-only access to the internal app storage via the
+ * {@link OnDeviceIntelligenceService}. This is an asynchronous alternative for
+ * {@link #openFileInput(String)}.
+ *
+ * @param fileName File name relative to the {@link Context#getFilesDir()}.
+ * @param resultConsumer Consumer to populate the corresponding file descriptor in.
+ */
+ public final void getReadOnlyFileDescriptor(@NonNull String fileName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<ParcelFileDescriptor> resultConsumer) throws FileNotFoundException {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ try {
+ mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ future.whenCompleteAsync((pfd, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Failure when reading file: " + fileName + err);
+ executor.execute(() -> resultConsumer.accept(null));
+ } else {
+ executor.execute(
+ () -> resultConsumer.accept(pfd));
+ }
+ }, executor);
+ }
+
+ /**
+ * Provides access to all file streams required for feature via the
+ * {@link OnDeviceIntelligenceService}.
+ *
+ * @param feature Feature for which the associated files should be fetched.
+ * @param executor Executor to run the consumer callback on.
+ * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input
+ * stream.
+ */
+ public final void fetchFeatureFileDescriptorMap(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Map<String, ParcelFileDescriptor>> resultConsumer) {
+ try {
+ mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature,
+ wrapAsRemoteCallback(resultConsumer, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ /**
+ * Returns the {@link Executor} to use for incoming IPC from request sender into your service
+ * implementation. For e.g. see
+ * {@link ProcessingCallback#onDataAugmentRequest(Bundle,
+ * Consumer)} where we use the executor to populate the consumer.
+ * <p>
+ * Override this method in your {@link OnDeviceSandboxedInferenceService} implementation to
+ * provide the executor you want to use for incoming IPC.
+ *
+ * @return the {@link Executor} to use for incoming IPC from {@link OnDeviceIntelligenceManager}
+ * to {@link OnDeviceSandboxedInferenceService}.
+ */
+ @SuppressLint("OnNameExpected")
+ @NonNull
+ public Executor getCallbackExecutor() {
+ return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+ }
+
+
+ private RemoteCallback wrapAsRemoteCallback(
+ @NonNull Consumer<Map<String, ParcelFileDescriptor>> resultConsumer,
+ @NonNull Executor executor) {
+ return new RemoteCallback(result -> {
+ if (result == null) {
+ executor.execute(() -> resultConsumer.accept(new HashMap<>()));
+ } else {
+ Map<String, ParcelFileDescriptor> pfdMap = new HashMap<>();
+ result.keySet().forEach(key ->
+ pfdMap.put(key, result.getParcelable(key,
+ ParcelFileDescriptor.class)));
+ executor.execute(() -> resultConsumer.accept(pfdMap));
+ }
+ });
+ }
+
+ private ProcessingCallback wrapResponseCallback(
+ IResponseCallback callback) {
+ return new ProcessingCallback() {
+ @Override
+ public void onResult(@NonNull Bundle result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onDataAugmentRequest(@NonNull Bundle content,
+ @NonNull Consumer<Bundle> contentCallback) {
+ try {
+ callback.onDataAugmentRequest(content, wrapRemoteCallback(contentCallback));
+
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending augment request: " + e);
+ }
+ }
+ };
+ }
+
+ private StreamingProcessingCallback wrapStreamingResponseCallback(
+ IStreamingResponseCallback callback) {
+ return new StreamingProcessingCallback() {
+ @Override
+ public void onPartialResult(@NonNull Bundle partialResult) {
+ try {
+ callback.onNewContent(partialResult);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onResult(@NonNull Bundle result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onDataAugmentRequest(@NonNull Bundle content,
+ @NonNull Consumer<Bundle> contentCallback) {
+ try {
+ callback.onDataAugmentRequest(content, wrapRemoteCallback(contentCallback));
+
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending augment request: " + e);
+ }
+ }
+ };
+ }
+
+ private RemoteCallback wrapRemoteCallback(
+ @NonNull Consumer<Bundle> contentCallback) {
+ return new RemoteCallback(
+ result -> {
+ if (result != null) {
+ getCallbackExecutor().execute(() -> contentCallback.accept(
+ result.getParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY,
+ Bundle.class)));
+ } else {
+ getCallbackExecutor().execute(
+ () -> contentCallback.accept(null));
+ }
+ });
+ }
+
+ private OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> wrapTokenInfoCallback(
+ ITokenInfoCallback tokenInfoCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(TokenInfo tokenInfo) {
+ try {
+ tokenInfoCallback.onSuccess(tokenInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceException exception) {
+ try {
+ tokenInfoCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending failure: " + e);
+ }
+ }
+ };
+ }
+
+ @NonNull
+ private static OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> wrapOutcomeReceiver(
+ IProcessingUpdateStatusCallback callback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull PersistableBundle result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceException error) {
+ try {
+ callback.onFailure(error.getErrorCode(), error.getMessage());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending exception details: " + e);
+ }
+ }
+ };
+ }
+
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java
new file mode 100644
index 000000000000..95fb2888a3e9
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java
@@ -0,0 +1,113 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback functions used for feature downloading via the
+ * {@link OnDeviceIntelligenceManager#requestFeatureDownload}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface DownloadCallback {
+ int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0;
+
+ /**
+ * Sent when feature download could not succeed due to there being no available disk space on
+ * the device.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1;
+
+ /**
+ * Sent when feature download could not succeed due to a network error.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2;
+
+ /**
+ * Sent when feature download has been initiated already, hence no need to request download
+ * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check if
+ * download has been completed.
+ */
+ int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3;
+
+ /**
+ * Sent when feature download did not start due to errors (e.g. remote exception of features not
+ * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check
+ * if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}.
+ */
+ int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4;
+
+ /** @hide */
+ @IntDef(value = {
+ DOWNLOAD_FAILURE_STATUS_UNKNOWN,
+ DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE,
+ DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE,
+ DOWNLOAD_FAILURE_STATUS_DOWNLOADING,
+ DOWNLOAD_FAILURE_STATUS_UNAVAILABLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DownloadFailureStatus {
+ }
+
+ /**
+ * Called when model download started properly.
+ *
+ * @param bytesToDownload the total bytes to be downloaded for this {@link Feature}
+ */
+ default void onDownloadStarted(long bytesToDownload) {
+ }
+
+ /**
+ * Called when model download failed.
+ *
+ * @param failureStatus the download failure status
+ * @param errorMessage the error message associated with the download failure
+ */
+ void onDownloadFailed(
+ @DownloadFailureStatus int failureStatus,
+ @Nullable String errorMessage,
+ @NonNull PersistableBundle errorParams);
+
+ /**
+ * Called when model download is in progress.
+ *
+ * @param totalBytesDownloaded the already downloaded bytes for this {@link Feature}
+ */
+ default void onDownloadProgress(long totalBytesDownloaded) {
+ }
+
+ /**
+ * Called when model download is completed. The remote implementation can populate any
+ * associated download params like file stats etc. in this callback to inform the client.
+ *
+ * @param downloadParams params containing info about the completed download.
+ */
+ void onDownloadCompleted(@NonNull PersistableBundle downloadParams);
+}
diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl
index 18494d754674..18494d754674 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl
diff --git a/core/java/android/app/ondeviceintelligence/Feature.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java
index bcc56073e51c..bcc56073e51c 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl
index 0589bf8bacb9..0589bf8bacb9 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java
index 44930f2c34f4..0ee0cc34c512 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -19,13 +19,12 @@ package android.app.ondeviceintelligence;
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import androidx.annotation.IntDef;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -69,7 +68,7 @@ public final class FeatureDetails implements Parcelable {
FEATURE_STATUS_DOWNLOADING,
FEATURE_STATUS_AVAILABLE,
FEATURE_STATUS_SERVICE_UNAVAILABLE
- }, open = true)
+ })
@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
new file mode 100644
index 000000000000..1fe201f8f1f8
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+oneway interface ICancellationSignal {
+ void cancel();
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
new file mode 100644
index 000000000000..2d7ea1a7b016
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface for Download callback to be passed onto service implementation,
+ *
+ * @hide
+ */
+oneway interface IDownloadCallback {
+ void onDownloadStarted(long bytesToDownload) = 1;
+ void onDownloadProgress(long bytesDownloaded) = 2;
+ void onDownloadFailed(int failureStatus, String errorMessage, in PersistableBundle errorParams) = 3;
+ void onDownloadCompleted(in PersistableBundle downloadParams) = 4;
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
new file mode 100644
index 000000000000..2e056926e400
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving a feature for the given identifier.
+ *
+ * @hide
+ */
+oneway interface IFeatureCallback {
+ void onSuccess(in Feature result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
new file mode 100644
index 000000000000..8688028743d7
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving details about a given feature. .
+ *
+ * @hide
+ */
+oneway interface IFeatureDetailsCallback {
+ void onSuccess(in FeatureDetails result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
new file mode 100644
index 000000000000..7e5eb57bbc4a
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
@@ -0,0 +1,15 @@
+package android.app.ondeviceintelligence;
+
+import java.util.List;
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving list of supported features.
+ *
+ * @hide
+ */
+oneway interface IListFeaturesCallback {
+ void onSuccess(in List<Feature> result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
index 1977a3923578..1977a3923578 100644
--- a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
new file mode 100644
index 000000000000..03946eebd40b
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+* Signal to provide to the remote implementation in context of a given request or
+* feature specific event.
+*
+* @hide
+*/
+
+oneway interface IProcessingSignal {
+ void sendSignal(in PersistableBundle actionParams) = 2;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
new file mode 100644
index 000000000000..6f07693dd39c
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
@@ -0,0 +1,24 @@
+/*
+* Copyright 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 android.app.ondeviceintelligence;
+
+import android.os.Bundle;
+
+/* @hide */
+oneway interface IRemoteCallback {
+ void sendResult(in Bundle data);
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl
new file mode 100644
index 000000000000..270b600e2de5
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl
@@ -0,0 +1,16 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+/**
+ * Interface for a IResponseCallback for receiving response from on-device intelligence service.
+ *
+ * @hide
+ */
+oneway interface IResponseCallback {
+ void onSuccess(in Bundle resultBundle) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+ void onDataAugmentRequest(in Bundle processedContent, in RemoteCallback responseCallback) = 3;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
new file mode 100644
index 000000000000..3e902405f3e0
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
@@ -0,0 +1,18 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.Bundle;
+
+
+/**
+ * This callback is a streaming variant of {@link IResponseCallback}.
+ *
+ * @hide
+ */
+oneway interface IStreamingResponseCallback {
+ void onNewContent(in Bundle processedResult) = 1;
+ void onSuccess(in Bundle result) = 2;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 3;
+ void onDataAugmentRequest(in Bundle processedContent, in RemoteCallback responseCallback) = 4;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
new file mode 100644
index 000000000000..958bef0a93e0
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.app.ondeviceintelligence.TokenInfo;
+
+/**
+ * Interface for receiving the token info of a request for a given feature.
+ *
+ * @hide
+ */
+oneway interface ITokenInfoCallback {
+ void onSuccess(in TokenInfo tokenInfo) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl
index 6d70fc4577a2..6d70fc4577a2 100644
--- a/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
new file mode 100644
index 000000000000..cae8db27a435
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
@@ -0,0 +1,220 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the information related to an inference event to track the resource usage
+ * as a function of inference time.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+public final class InferenceInfo implements Parcelable {
+
+ /**
+ * Uid for the caller app.
+ */
+ private final int uid;
+
+ /**
+ * Inference start time (milliseconds from the epoch time).
+ */
+ private final long startTimeMs;
+
+ /**
+ * Inference end time (milliseconds from the epoch time).
+ */
+ private final long endTimeMs;
+
+ /**
+ * Suspended time in milliseconds.
+ */
+ private final long suspendedTimeMs;
+
+ /**
+ * Constructs an InferenceInfo object with the specified parameters.
+ *
+ * @param uid Uid for the caller app.
+ * @param startTimeMs Inference start time (milliseconds from the epoch time).
+ * @param endTimeMs Inference end time (milliseconds from the epoch time).
+ * @param suspendedTimeMs Suspended time in milliseconds.
+ */
+ InferenceInfo(int uid, long startTimeMs, long endTimeMs,
+ long suspendedTimeMs) {
+ this.uid = uid;
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.suspendedTimeMs = suspendedTimeMs;
+ }
+
+ /**
+ * Constructs an InferenceInfo object from a Parcel.
+ *
+ * @param in The Parcel to read the object's data from.
+ */
+ private InferenceInfo(@NonNull Parcel in) {
+ uid = in.readInt();
+ startTimeMs = in.readLong();
+ endTimeMs = in.readLong();
+ suspendedTimeMs = in.readLong();
+ }
+
+
+ /**
+ * Writes the object's data to the provided Parcel.
+ *
+ * @param dest The Parcel to write the object's data to.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(uid);
+ dest.writeLong(startTimeMs);
+ dest.writeLong(endTimeMs);
+ dest.writeLong(suspendedTimeMs);
+ }
+
+ /**
+ * Returns the UID for the caller app.
+ *
+ * @return the UID for the caller app.
+ */
+ public int getUid() {
+ return uid;
+ }
+
+ /**
+ * Returns the inference start time in milliseconds from the epoch time.
+ *
+ * @return the inference start time in milliseconds from the epoch time.
+ */
+ @CurrentTimeMillisLong
+ public long getStartTimeMillis() {
+ return startTimeMs;
+ }
+
+ /**
+ * Returns the inference end time in milliseconds from the epoch time.
+ *
+ * @return the inference end time in milliseconds from the epoch time.
+ */
+ @CurrentTimeMillisLong
+ public long getEndTimeMillis() {
+ return endTimeMs;
+ }
+
+ /**
+ * Returns the suspended time in milliseconds.
+ *
+ * @return the suspended time in milliseconds.
+ */
+ @CurrentTimeMillisLong
+ public long getSuspendedTimeMillis() {
+ return suspendedTimeMs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+
+ public static final @android.annotation.NonNull Parcelable.Creator<InferenceInfo> CREATOR
+ = new Parcelable.Creator<InferenceInfo>() {
+ @Override
+ public InferenceInfo[] newArray(int size) {
+ return new InferenceInfo[size];
+ }
+
+ @Override
+ public InferenceInfo createFromParcel(@android.annotation.NonNull Parcel in) {
+ return new InferenceInfo(in);
+ }
+ };
+
+ /**
+ * Builder class for creating instances of {@link InferenceInfo}.
+ */
+ public static final class Builder {
+ private final int uid;
+ private long startTimeMs;
+ private long endTimeMs;
+ private long suspendedTimeMs;
+
+ /**
+ * Provides a builder instance to create a InferenceInfo for given caller uid.
+ *
+ * @param uid the caller uid associated with the inference info.
+ */
+ public Builder(int uid) {
+ this.uid = uid;
+ }
+
+ /**
+ * Sets the inference start time in milliseconds from the epoch time.
+ *
+ * @param startTimeMs the inference start time in milliseconds from the epoch time.
+ * @return the Builder instance.
+ */
+ public @NonNull Builder setStartTimeMillis(@CurrentTimeMillisLong long startTimeMs) {
+ this.startTimeMs = startTimeMs;
+ return this;
+ }
+
+ /**
+ * Sets the inference end time in milliseconds from the epoch time.
+ *
+ * @param endTimeMs the inference end time in milliseconds from the epoch time.
+ * @return the Builder instance.
+ */
+ public @NonNull Builder setEndTimeMillis(@CurrentTimeMillisLong long endTimeMs) {
+ this.endTimeMs = endTimeMs;
+ return this;
+ }
+
+ /**
+ * Sets the suspended time in milliseconds.
+ *
+ * @param suspendedTimeMs the suspended time in milliseconds.
+ * @return the Builder instance.
+ */
+ public @NonNull Builder setSuspendedTimeMillis(@CurrentTimeMillisLong long suspendedTimeMs) {
+ this.suspendedTimeMs = suspendedTimeMs;
+ return this;
+ }
+
+ /**
+ * Builds and returns an instance of {@link InferenceInfo}.
+ *
+ * @return an instance of {@link InferenceInfo}.
+ */
+ public @NonNull InferenceInfo build() {
+ return new InferenceInfo(uid, startTimeMs, endTimeMs,
+ suspendedTimeMs);
+ }
+ }
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
new file mode 100644
index 000000000000..2881c9d217dc
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Exception type to be used for errors related to on-device intelligence system service with
+ * appropriate error code.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceException extends Exception {
+
+ public static final int PROCESSING_ERROR_UNKNOWN = 1;
+
+ /** Request passed contains bad data for e.g. format. */
+ public static final int PROCESSING_ERROR_BAD_DATA = 2;
+
+ /** Bad request for inputs. */
+ public static final int PROCESSING_ERROR_BAD_REQUEST = 3;
+
+ /** Whole request was classified as not safe, and no response will be generated. */
+ public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4;
+
+ /** Underlying processing encountered an error and failed to compute results. */
+ public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5;
+
+ /** Encountered an error while performing IPC */
+ public static final int PROCESSING_ERROR_IPC_ERROR = 6;
+
+ /** Request was cancelled either by user signal or by the underlying implementation. */
+ public static final int PROCESSING_ERROR_CANCELLED = 7;
+
+ /** Underlying processing in the remote implementation is not available. */
+ public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8;
+
+ /** The service is currently busy. Callers should retry with exponential backoff. */
+ public static final int PROCESSING_ERROR_BUSY = 9;
+
+ /** Something went wrong with safety classification service. */
+ public static final int PROCESSING_ERROR_SAFETY_ERROR = 10;
+
+ /** Response generated was classified unsafe. */
+ public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11;
+
+ /** Request is too large to be processed. */
+ public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12;
+
+ /** Inference suspended so that higher-priority inference can run. */
+ public static final int PROCESSING_ERROR_SUSPENDED = 13;
+
+ /**
+ * Underlying processing encountered an internal error, like a violated precondition
+ * .
+ */
+ public static final int PROCESSING_ERROR_INTERNAL = 14;
+
+ /**
+ * The processing was not able to be passed on to the remote implementation, as the
+ * service
+ * was unavailable.
+ */
+ public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15;
+ /**
+ * Error code returned when the OnDeviceIntelligenceManager service is unavailable.
+ */
+ public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 100;
+
+ /**
+ * The connection to remote service failed and the processing state could not be updated.
+ */
+ public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 200;
+
+
+ /**
+ * Error code associated with the on-device intelligence failure.
+ *
+ * @hide
+ */
+ @IntDef(
+ value = {
+ PROCESSING_ERROR_UNKNOWN,
+ PROCESSING_ERROR_BAD_DATA,
+ PROCESSING_ERROR_BAD_REQUEST,
+ PROCESSING_ERROR_REQUEST_NOT_SAFE,
+ PROCESSING_ERROR_COMPUTE_ERROR,
+ PROCESSING_ERROR_IPC_ERROR,
+ PROCESSING_ERROR_CANCELLED,
+ PROCESSING_ERROR_NOT_AVAILABLE,
+ PROCESSING_ERROR_BUSY,
+ PROCESSING_ERROR_SAFETY_ERROR,
+ PROCESSING_ERROR_RESPONSE_NOT_SAFE,
+ PROCESSING_ERROR_REQUEST_TOO_LARGE,
+ PROCESSING_ERROR_SUSPENDED,
+ PROCESSING_ERROR_INTERNAL,
+ PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ PROCESSING_UPDATE_STATUS_CONNECTION_FAILED
+ })
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface OnDeviceIntelligenceError {
+ }
+
+ private final int mErrorCode;
+ private final PersistableBundle mErrorParams;
+
+ /** Returns the error code of the exception. */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /** Returns the error params of the exception. */
+ @NonNull
+ public PersistableBundle getErrorParams() {
+ return mErrorParams;
+ }
+
+ /**
+ * Creates a new OnDeviceIntelligenceException with the specified error code, error message and
+ * error params.
+ *
+ * @param errorCode The error code.
+ * @param errorMessage The error message.
+ * @param errorParams The error params.
+ */
+ public OnDeviceIntelligenceException(
+ @OnDeviceIntelligenceError int errorCode, @NonNull String errorMessage,
+ @NonNull PersistableBundle errorParams) {
+ super(errorMessage);
+ this.mErrorCode = errorCode;
+ this.mErrorParams = errorParams;
+ }
+
+ /**
+ * Creates a new OnDeviceIntelligenceException with the specified error code and error params.
+ *
+ * @param errorCode The error code.
+ * @param errorParams The error params.
+ */
+ public OnDeviceIntelligenceException(
+ @OnDeviceIntelligenceError int errorCode,
+ @NonNull PersistableBundle errorParams) {
+ this.mErrorCode = errorCode;
+ this.mErrorParams = errorParams;
+ }
+
+ /**
+ * Creates a new OnDeviceIntelligenceException with the specified error code and error message.
+ *
+ * @param errorCode The error code.
+ * @param errorMessage The error message.
+ */
+ public OnDeviceIntelligenceException(
+ @OnDeviceIntelligenceError int errorCode, @NonNull String errorMessage) {
+ super(errorMessage);
+ this.mErrorCode = errorCode;
+ this.mErrorParams = new PersistableBundle();
+ }
+
+ /**
+ * Creates a new OnDeviceIntelligenceException with the specified error code.
+ *
+ * @param errorCode The error code.
+ */
+ public OnDeviceIntelligenceException(
+ @OnDeviceIntelligenceError int errorCode) {
+ this.mErrorCode = errorCode;
+ this.mErrorParams = new PersistableBundle();
+ }
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
new file mode 100644
index 000000000000..7d35dd7f2237
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for OnDeviceIntelligence service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceFrameworkInitializer {
+ private OnDeviceIntelligenceFrameworkInitializer() {
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers
+ * OnDeviceIntelligence service to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides {@link
+ * SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(Context.ON_DEVICE_INTELLIGENCE_SERVICE,
+ OnDeviceIntelligenceManager.class,
+ (context, serviceBinder) -> {
+ IOnDeviceIntelligenceManager manager =
+ IOnDeviceIntelligenceManager.Stub.asInterface(serviceBinder);
+ return new OnDeviceIntelligenceManager(context, manager);
+ });
+ }
+} \ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index 91651e317b18..78cf1d7a611b 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -23,6 +23,7 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -42,8 +43,6 @@ import android.os.RemoteException;
import android.system.OsConstants;
import android.util.Log;
-import androidx.annotation.IntDef;
-
import com.android.internal.infra.AndroidFuture;
import java.lang.annotation.ElementType;
@@ -537,7 +536,7 @@ public final class OnDeviceIntelligenceManager {
REQUEST_TYPE_INFERENCE,
REQUEST_TYPE_PREPARE,
REQUEST_TYPE_EMBEDDINGS
- }, open = true)
+ })
@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER,
ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java
new file mode 100644
index 000000000000..e50d6b1fa97a
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.InferenceParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ResponseParams;
+
+import java.util.function.Consumer;
+
+/**
+ * Callback to populate the processed response or any error that occurred during the
+ * request processing. This callback also provides a method to request additional data to be
+ * augmented to the request-processing, using the partial response that was already
+ * processed in the remote implementation.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface ProcessingCallback {
+ /**
+ * Invoked when request has been processed and result is ready to be propagated to the
+ * caller.
+ *
+ * @param result Response to be passed as a result.
+ */
+ void onResult(@NonNull @ResponseParams Bundle result);
+
+ /**
+ * Called when the request processing fails. The failure details are indicated by the
+ * {@link OnDeviceIntelligenceException} passed as an argument to this method.
+ *
+ * @param error An exception with more details about the error that occurred.
+ */
+ void onError(@NonNull OnDeviceIntelligenceException error);
+
+ /**
+ * Callback to be invoked in cases where the remote service needs to perform retrieval or
+ * transformation operations based on a partially processed request, in order to augment the
+ * final response, by using the additional context sent via this callback.
+ *
+ * @param processedContent The content payload that should be used to augment ongoing request.
+ * @param contentConsumer The augmentation data that should be sent to remote
+ * service for further processing a request. Bundle passed in here is
+ * expected to be non-null or EMPTY when there is no response.
+ */
+ default void onDataAugmentRequest(
+ @NonNull @ResponseParams Bundle processedContent,
+ @NonNull Consumer<@InferenceParams Bundle> contentConsumer) {
+ contentConsumer.accept(Bundle.EMPTY);
+ }
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java
new file mode 100644
index 000000000000..733f4fad96f4
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java
@@ -0,0 +1,225 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayDeque;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A signal to perform orchestration actions on the inference and optionally receive a output about
+ * the result of the signal. This is an extension of {@link android.os.CancellationSignal}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class ProcessingSignal {
+ private final Object mLock = new Object();
+
+ private static final int MAX_QUEUE_SIZE = 10;
+
+ @GuardedBy("mLock")
+ private final ArrayDeque<PersistableBundle> mActionParamsQueue;
+
+ @GuardedBy("mLock")
+ private IProcessingSignal mRemote;
+
+ private OnProcessingSignalCallback mOnProcessingSignalCallback;
+ private Executor mExecutor;
+
+ public ProcessingSignal() {
+ mActionParamsQueue = new ArrayDeque<>(MAX_QUEUE_SIZE);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when processing signals are received.
+ */
+ public interface OnProcessingSignalCallback {
+ /**
+ * Called when a custom signal was received.
+ * This method allows the receiver to provide logic to be executed based on the signal
+ * received.
+ *
+ * @param actionParams Parameters for the signal in the form of a {@link PersistableBundle}.
+ */
+
+ void onSignalReceived(@NonNull PersistableBundle actionParams);
+ }
+
+
+ /**
+ * Sends a custom signal with the provided parameters. If there are multiple concurrent
+ * requests to this method, the actionParams are queued in a blocking fashion, in the order they
+ * are received.
+ *
+ * It also signals the remote callback
+ * with the same params if already configured, if not the action is queued to be sent when a
+ * remote is configured. Similarly, on the receiver side, the callback will be invoked if
+ * already set, if not all actions are queued to be sent to callback when it is set.
+ *
+ * @param actionParams Parameters for the signal.
+ */
+ public void sendSignal(@NonNull PersistableBundle actionParams) {
+ final OnProcessingSignalCallback callback;
+ final IProcessingSignal remote;
+ synchronized (mLock) {
+ if (mActionParamsQueue.size() > MAX_QUEUE_SIZE) {
+ throw new RuntimeException(
+ "Maximum actions that can be queued are : " + MAX_QUEUE_SIZE);
+ }
+
+ mActionParamsQueue.add(actionParams);
+ callback = mOnProcessingSignalCallback;
+ remote = mRemote;
+
+ if (callback != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(
+ () -> callback.onSignalReceived(params));
+ }
+ }
+ if (remote != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sets the processing signal callback to be called when signals are received.
+ *
+ * This method is intended to be used by the recipient of a processing signal
+ * such as the remote implementation in
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} to handle
+ * processing signals while performing a long-running operation. This method is not
+ * intended to be used by the caller themselves.
+ *
+ * If {@link ProcessingSignal#sendSignal} has already been called, then the provided callback
+ * is invoked immediately and all previously queued actions are passed to remote signal.
+ *
+ * This method is guaranteed that the callback will not be called after it
+ * has been removed.
+ *
+ * @param callback The processing signal callback, or null to remove the current callback.
+ * @param executor Executor to the run the callback methods on.
+ */
+ public void setOnProcessingSignalCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable OnProcessingSignalCallback callback) {
+ Objects.requireNonNull(executor);
+ synchronized (mLock) {
+ if (mOnProcessingSignalCallback == callback) {
+ return;
+ }
+
+ mOnProcessingSignalCallback = callback;
+ mExecutor = executor;
+ if (callback == null || mActionParamsQueue.isEmpty()) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(() -> callback.onSignalReceived(params));
+ }
+ }
+ }
+
+ /**
+ * Sets the remote transport.
+ *
+ * If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also
+ * sequentially sent to the configured remote.
+ *
+ * This method guarantees that the remote transport will not be called after it
+ * has been removed.
+ *
+ * @param remote The remote transport, or null to remove.
+ * @hide
+ */
+ void setRemote(IProcessingSignal remote) {
+ synchronized (mLock) {
+ mRemote = remote;
+ if (mActionParamsQueue.isEmpty() || remote == null) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to send action to remote signal", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a transport that can be returned back to the caller of
+ * a Binder function and subsequently used to dispatch a processing signal.
+ *
+ * @return The new processing signal transport.
+ * @hide
+ */
+ public static IProcessingSignal createTransport() {
+ return new Transport();
+ }
+
+ /**
+ * Given a locally created transport, returns its associated processing signal.
+ *
+ * @param transport The locally created transport, or null if none.
+ * @return The associated processing signal, or null if none.
+ * @hide
+ */
+ public static ProcessingSignal fromTransport(IProcessingSignal transport) {
+ if (transport instanceof Transport) {
+ return ((Transport) transport).mProcessingSignal;
+ }
+ return null;
+ }
+
+ private static final class Transport extends IProcessingSignal.Stub {
+ final ProcessingSignal mProcessingSignal = new ProcessingSignal();
+
+ @Override
+ public void sendSignal(PersistableBundle actionParams) {
+ mProcessingSignal.sendSignal(actionParams);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
new file mode 100644
index 000000000000..7ee2af7376ed
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ResponseParams;
+
+/**
+ * Streaming variant of {@link ProcessingCallback} to populate response while processing a given
+ * request, possibly in chunks to provide a async processing behaviour to the caller.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface StreamingProcessingCallback extends ProcessingCallback {
+ /**
+ * Callback that would be invoked when a part of the response i.e. some response is
+ * already processed, and needs to be passed onto the caller.
+ */
+ void onPartialResult(@NonNull @ResponseParams Bundle partialResult);
+}
diff --git a/core/java/android/app/ondeviceintelligence/TokenInfo.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl
index 2c19c1eb246c..2c19c1eb246c 100644
--- a/core/java/android/app/ondeviceintelligence/TokenInfo.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java
new file mode 100644
index 000000000000..035cc4b365b5
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * This class is used to provide a token count response for the
+ * {@link OnDeviceIntelligenceManager#requestTokenInfo} outcome receiver.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class TokenInfo implements Parcelable {
+ private final long mCount;
+ private final PersistableBundle mInfoParams;
+
+ /**
+ * Construct a token count using the count value and associated params.
+ */
+ public TokenInfo(long count, @NonNull PersistableBundle persistableBundle) {
+ this.mCount = count;
+ mInfoParams = persistableBundle;
+ }
+
+ /**
+ * Construct a token count using the count value.
+ */
+ public TokenInfo(long count) {
+ this.mCount = count;
+ this.mInfoParams = new PersistableBundle();
+ }
+
+ /**
+ * Returns the token count associated with a request payload.
+ */
+ public long getCount() {
+ return mCount;
+ }
+
+ /**
+ * Returns the params representing token info.
+ */
+ @NonNull
+ public PersistableBundle getInfoParams() {
+ return mInfoParams;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mCount);
+ dest.writePersistableBundle(mInfoParams);
+ }
+
+ public static final @NonNull Parcelable.Creator<TokenInfo> CREATOR
+ = new Parcelable.Creator<>() {
+ @Override
+ public TokenInfo[] newArray(int size) {
+ return new TokenInfo[size];
+ }
+
+ @Override
+ public TokenInfo createFromParcel(@NonNull Parcel in) {
+ return new TokenInfo(in.readLong(), in.readPersistableBundle());
+ }
+ };
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java
new file mode 100644
index 000000000000..2916f030e3d0
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.app.ondeviceintelligence.utils;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import java.util.function.Supplier;
+
+/**
+ * Collection of utilities for {@link Binder} and related classes.
+ * @hide
+ */
+public class BinderUtils {
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
+ *
+ * Any exception thrown by the given action will be caught and rethrown after the call to
+ * {@link Binder#restoreCallingIdentity}
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T extends Exception> void withCleanCallingIdentity(
+ @NonNull ThrowingRunnable<T> action) throws T {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ action.run();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * Like a Runnable, but declared to throw an exception.
+ *
+ * @param <T> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingRunnable<T extends Exception> {
+ /** @see java.lang.Runnable */
+ void run() throws T;
+ }
+
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} returning the
+ * result.
+ *
+ * <p>Any exception thrown by the given action will be caught and rethrown after
+ * the call to {@link Binder#restoreCallingIdentity}.
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T, E extends Exception> T withCleanCallingIdentity(
+ @NonNull ThrowingSupplier<T, E> action) throws E {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return action.get();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * An equivalent of {@link Supplier}
+ *
+ * @param <T> The class which is declared to be returned.
+ * @param <E> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingSupplier<T, E extends Exception> {
+ /** @see java.util.function.Supplier */
+ T get() throws E;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
index 45c43502d6de..45c43502d6de 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
index 1af3b0f374f1..1af3b0f374f1 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
new file mode 100644
index 000000000000..7ead8690abb4
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
@@ -0,0 +1,14 @@
+package android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving status from a updateProcessingState call from on-device intelligence
+ * service.
+ *
+ * @hide
+ */
+interface IProcessingUpdateStatusCallback {
+ void onSuccess(in PersistableBundle statusParams) = 1;
+ void onFailure(int errorCode, in String errorMessage) = 2;
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
new file mode 100644
index 000000000000..32a8a6a70406
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.os.Bundle;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
+
+
+/**
+ * Interface for a concrete implementation to provide methods to update state of a remote service.
+ *
+ * @hide
+ */
+oneway interface IRemoteProcessingService {
+ void updateProcessingState(in Bundle processingState,
+ in IProcessingUpdateStatusCallback callback);
+}
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
index a6f49e17d80e..a6f49e17d80e 100644
--- a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index d82fe1ca885c..618d2a096916 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -27,7 +27,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Service;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
@@ -260,10 +259,7 @@ public abstract class OnDeviceIntelligenceService extends Service {
* Using this signal to assertively a signal each time service binds successfully, used only in
* tests to get a signal that service instance is ready. This is needed because we cannot rely
* on {@link #onCreate} or {@link #onBind} to be invoke on each binding.
- *
- * @hide
*/
- @TestApi
public void onReady() {
}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index 3181556eded7..949fb8ddda9d 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -21,8 +21,8 @@ import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import android.annotation.CallbackExecutor;
import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -74,7 +74,7 @@ import java.util.function.Consumer;
/**
* Abstract base class for performing inference in a isolated process. This service exposes its
- * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ * methods via {@link OnDeviceIntelligenceManager}.
*
* <p> A service that provides methods to perform on-device inference both in streaming and
* non-streaming fashion. Also, provides a way to register a storage service that will be used to
@@ -471,7 +471,7 @@ public abstract class OnDeviceSandboxedInferenceService extends Service {
IResponseCallback callback) {
return new ProcessingCallback() {
@Override
- public void onResult(@androidx.annotation.NonNull Bundle result) {
+ public void onResult(@NonNull Bundle result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -507,7 +507,7 @@ public abstract class OnDeviceSandboxedInferenceService extends Service {
IStreamingResponseCallback callback) {
return new StreamingProcessingCallback() {
@Override
- public void onPartialResult(@androidx.annotation.NonNull Bundle partialResult) {
+ public void onPartialResult(@NonNull Bundle partialResult) {
try {
callback.onNewContent(partialResult);
} catch (RemoteException e) {
@@ -516,7 +516,7 @@ public abstract class OnDeviceSandboxedInferenceService extends Service {
}
@Override
- public void onResult(@androidx.annotation.NonNull Bundle result) {
+ public void onResult(@NonNull Bundle result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -549,7 +549,7 @@ public abstract class OnDeviceSandboxedInferenceService extends Service {
}
private RemoteCallback wrapRemoteCallback(
- @androidx.annotation.NonNull Consumer<Bundle> contentCallback) {
+ @NonNull Consumer<Bundle> contentCallback) {
return new RemoteCallback(
result -> {
if (result != null) {
@@ -604,7 +604,7 @@ public abstract class OnDeviceSandboxedInferenceService extends Service {
@Override
public void onError(
- @androidx.annotation.NonNull OnDeviceIntelligenceException error) {
+ @NonNull OnDeviceIntelligenceException error) {
try {
callback.onFailure(error.getErrorCode(), error.getMessage());
} catch (RemoteException e) {
diff --git a/packages/NeuralNetworks/service/Android.bp b/packages/NeuralNetworks/service/Android.bp
new file mode 100644
index 000000000000..cfdc1af6857f
--- /dev/null
+++ b/packages/NeuralNetworks/service/Android.bp
@@ -0,0 +1,38 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "service-ondeviceintelligence-sources",
+ srcs: [
+ "module/java/**/*.java",
+ ],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
+ ],
+}
+
+filegroup {
+ name: "service-ondeviceintelligence-sources-platform",
+ srcs: [
+ "platform/java/**/*.java",
+ ],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ ],
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java
new file mode 100644
index 000000000000..2626cc880e09
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -0,0 +1,415 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.system.OsConstants.F_GETFL;
+import static android.system.OsConstants.O_ACCMODE;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.PROT_READ;
+
+import android.annotation.SuppressLint;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.InferenceParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ResponseParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.TokenInfo;
+import android.database.CursorWindow;
+import android.graphics.Bitmap;
+import android.os.BadParcelableException;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.modules.utils.AndroidFuture;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
+ * some known types.
+ *
+ * @hide
+ */
+public class BundleUtil {
+ private static final String TAG = "BundleUtil";
+
+ /**
+ * Validation of the inference request payload as described in {@link InferenceParams}
+ * description.
+ *
+ * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+ */
+ public static void sanitizeInferenceParams(
+ @InferenceParams Bundle bundle) {
+ ensureValidBundle(bundle);
+
+ if (!bundle.hasFileDescriptors()) {
+ return; //safe to exit if there are no FDs and Binders
+ }
+
+ for (String key : bundle.keySet()) {
+ Object obj = bundle.get(key);
+ if (obj == null) {
+ /* Null value here could also mean deserializing a custom parcelable has failed,
+ * and since {@link Bundle} is marked as defusable in system-server - the
+ * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+ * instead. We want to ensure cleanup of null entries in such case.
+ */
+ bundle.putParcelable(key, null);
+ continue;
+ }
+ if (canMarshall(obj) || obj instanceof CursorWindow) {
+ continue;
+ }
+ if (obj instanceof Bundle) {
+ sanitizeInferenceParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
+ validatePfdReadOnly((ParcelFileDescriptor) obj);
+ } else if (obj instanceof SharedMemory) {
+ ((SharedMemory) obj).setProtect(PROT_READ);
+ } else if (obj instanceof Bitmap) {
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
+ } else {
+ throw new BadParcelableException(
+ "Unsupported Parcelable type encountered in the Bundle: "
+ + obj.getClass().getSimpleName());
+ }
+ }
+ }
+
+ /**
+ * Validation of the inference request payload as described in {@link ResponseParams}
+ * description.
+ *
+ * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+ */
+ public static void sanitizeResponseParams(
+ @ResponseParams Bundle bundle) {
+ ensureValidBundle(bundle);
+
+ if (!bundle.hasFileDescriptors()) {
+ return; //safe to exit if there are no FDs and Binders
+ }
+
+ for (String key : bundle.keySet()) {
+ Object obj = bundle.get(key);
+ if (obj == null) {
+ /* Null value here could also mean deserializing a custom parcelable has failed,
+ * and since {@link Bundle} is marked as defusable in system-server - the
+ * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+ * instead. We want to ensure cleanup of null entries in such case.
+ */
+ bundle.putParcelable(key, null);
+ continue;
+ }
+ if (canMarshall(obj)) {
+ continue;
+ }
+
+ if (obj instanceof Bundle) {
+ sanitizeResponseParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
+ validatePfdReadOnly((ParcelFileDescriptor) obj);
+ } else if (obj instanceof Bitmap) {
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
+ } else {
+ throw new BadParcelableException(
+ "Unsupported Parcelable type encountered in the Bundle: "
+ + obj.getClass().getSimpleName());
+ }
+ }
+ }
+
+ /**
+ * Validation of the inference request payload as described in {@link StateParams}
+ * description.
+ *
+ * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+ */
+ public static void sanitizeStateParams(
+ @StateParams Bundle bundle) {
+ ensureValidBundle(bundle);
+
+ if (!bundle.hasFileDescriptors()) {
+ return; //safe to exit if there are no FDs and Binders
+ }
+
+ for (String key : bundle.keySet()) {
+ Object obj = bundle.get(key);
+ if (obj == null) {
+ /* Null value here could also mean deserializing a custom parcelable has failed,
+ * and since {@link Bundle} is marked as defusable in system-server - the
+ * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+ * instead. We want to ensure cleanup of null entries in such case.
+ */
+ bundle.putParcelable(key, null);
+ continue;
+ }
+ if (canMarshall(obj)) {
+ continue;
+ }
+
+ if (obj instanceof ParcelFileDescriptor) {
+ validatePfdReadOnly((ParcelFileDescriptor) obj);
+ } else {
+ throw new BadParcelableException(
+ "Unsupported Parcelable type encountered in the Bundle: "
+ + obj.getClass().getSimpleName());
+ }
+ }
+ }
+
+
+ public static IStreamingResponseCallback wrapWithValidation(
+ IStreamingResponseCallback streamingResponseCallback,
+ Executor resourceClosingExecutor,
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
+ return new IStreamingResponseCallback.Stub() {
+ @Override
+ public void onNewContent(Bundle processedResult) throws RemoteException {
+ try {
+ sanitizeResponseParams(processedResult);
+ streamingResponseCallback.onNewContent(processedResult);
+ } finally {
+ resourceClosingExecutor.execute(() -> tryCloseResource(processedResult));
+ }
+ }
+
+ @Override
+ public void onSuccess(Bundle resultBundle)
+ throws RemoteException {
+ try {
+ sanitizeResponseParams(resultBundle);
+ streamingResponseCallback.onSuccess(resultBundle);
+ } finally {
+ inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
+ resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
+ }
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDataAugmentRequest(Bundle processedContent,
+ RemoteCallback remoteCallback)
+ throws RemoteException {
+ try {
+ sanitizeResponseParams(processedContent);
+ streamingResponseCallback.onDataAugmentRequest(processedContent,
+ new RemoteCallback(
+ augmentedData -> {
+ try {
+ sanitizeInferenceParams(augmentedData);
+ remoteCallback.sendResult(augmentedData);
+ } finally {
+ resourceClosingExecutor.execute(
+ () -> tryCloseResource(augmentedData));
+ }
+ }));
+ } finally {
+ resourceClosingExecutor.execute(() -> tryCloseResource(processedContent));
+ }
+ }
+ };
+ }
+
+ public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
+ Executor resourceClosingExecutor,
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
+ return new IResponseCallback.Stub() {
+ @Override
+ public void onSuccess(Bundle resultBundle)
+ throws RemoteException {
+ try {
+ sanitizeResponseParams(resultBundle);
+ responseCallback.onSuccess(resultBundle);
+ } finally {
+ inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
+ resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
+ }
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDataAugmentRequest(Bundle processedContent,
+ RemoteCallback remoteCallback)
+ throws RemoteException {
+ try {
+ sanitizeResponseParams(processedContent);
+ responseCallback.onDataAugmentRequest(processedContent, new RemoteCallback(
+ augmentedData -> {
+ try {
+ sanitizeInferenceParams(augmentedData);
+ remoteCallback.sendResult(augmentedData);
+ } finally {
+ resourceClosingExecutor.execute(
+ () -> tryCloseResource(augmentedData));
+ }
+ }));
+ } finally {
+ resourceClosingExecutor.execute(() -> tryCloseResource(processedContent));
+ }
+ }
+ };
+ }
+
+
+ public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
+ return new ITokenInfoCallback.Stub() {
+ @Override
+ public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
+ responseCallback.onSuccess(tokenInfo);
+ inferenceInfoStore.addInferenceInfoFromBundle(tokenInfo.getInfoParams());
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
+ throws RemoteException {
+ responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ };
+ }
+
+ private static boolean canMarshall(Object value) {
+ return (value instanceof byte[]) || (value instanceof Integer) || (value instanceof Long) ||
+ (value instanceof Double) || (value instanceof String) ||
+ (value instanceof int[]) || (value instanceof long[]) ||
+ (value instanceof double[]) || (value instanceof String[]) ||
+ (value instanceof PersistableBundle) || (value == null) ||
+ (value instanceof Boolean) || (value instanceof boolean[]);
+ }
+
+ @SuppressLint("NewApi")
+ private static void ensureValidBundle(Bundle bundle) {
+ if (bundle == null) {
+ throw new IllegalArgumentException("Request passed is expected to be non-null");
+ }
+
+ if (bundle.hasBinders() != Bundle.STATUS_BINDERS_NOT_PRESENT) {
+ throw new BadParcelableException("Bundle should not contain IBinder objects.");
+ }
+ }
+
+ private static void validateParcelableArray(Parcelable[] parcelables) {
+ if (parcelables.length > 0
+ && parcelables[0] instanceof ParcelFileDescriptor) {
+ // Safe to cast
+ validatePfdsReadOnly(parcelables);
+ } else if (parcelables.length > 0
+ && parcelables[0] instanceof Bitmap) {
+ validateBitmapsImmutable(parcelables);
+ } else {
+ throw new BadParcelableException(
+ "Could not cast to any known parcelable array");
+ }
+ }
+
+ public static void validatePfdsReadOnly(Parcelable[] pfds) {
+ for (Parcelable pfd : pfds) {
+ validatePfdReadOnly((ParcelFileDescriptor) pfd);
+ }
+ }
+
+ public static void validatePfdReadOnly(ParcelFileDescriptor pfd) {
+ if (pfd == null) {
+ return;
+ }
+ try {
+ int readMode = Os.fcntlInt(pfd.getFileDescriptor(), F_GETFL, 0) & O_ACCMODE;
+ if (readMode != O_RDONLY) {
+ throw new BadParcelableException(
+ "Bundle contains a parcel file descriptor which is not read-only.");
+ }
+ } catch (ErrnoException e) {
+ throw new BadParcelableException(
+ "Invalid File descriptor passed in the Bundle.");
+ }
+ }
+
+ private static void validateBitmap(Bitmap obj) {
+ if (obj.isMutable()) {
+ throw new BadParcelableException(
+ "Encountered a mutable Bitmap in the Bundle at key : " + obj);
+ }
+ }
+
+ private static void validateBitmapsImmutable(Parcelable[] bitmaps) {
+ for (Parcelable bitmap : bitmaps) {
+ validateBitmap((Bitmap) bitmap);
+ }
+ }
+
+ public static void tryCloseResource(Bundle bundle) {
+ if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) {
+ return;
+ }
+
+ for (String key : bundle.keySet()) {
+ Object obj = bundle.get(key);
+
+ try {
+ // TODO(b/329898589) : This can be cleaned up after the flag passing is fixed.
+ if (obj instanceof ParcelFileDescriptor) {
+ ((ParcelFileDescriptor) obj).close();
+ } else if (obj instanceof CursorWindow) {
+ ((CursorWindow) obj).close();
+ } else if (obj instanceof SharedMemory) {
+ // TODO(b/331796886) : Shared memory should honour parcelable flags.
+ ((SharedMemory) obj).close();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error closing resource with key: " + key, e);
+ }
+ }
+ }
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
new file mode 100644
index 000000000000..e8a1b322f661
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -0,0 +1,104 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.util.Base64;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * @hide
+ */
+public class InferenceInfoStore {
+ private static final String TAG = "InferenceInfoStore";
+ private final TreeSet<InferenceInfo> inferenceInfos;
+ private final long maxAgeMs;
+
+ public InferenceInfoStore(long maxAgeMs) {
+ this.maxAgeMs = maxAgeMs;
+ this.inferenceInfos = new TreeSet<>(
+ Comparator.comparingLong(InferenceInfo::getStartTimeMillis));
+ }
+
+ public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ return inferenceInfos.stream().filter(
+ info -> info.getStartTimeMillis() > startTimeEpochMillis).toList();
+ }
+
+ public void addInferenceInfoFromBundle(PersistableBundle pb) {
+ if (!pb.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+ return;
+ }
+
+ try {
+ String infoBytesBase64String = pb.getString(
+ OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+ if (infoBytesBase64String != null) {
+ byte[] infoBytes = Base64.decode(infoBytesBase64String, Base64.DEFAULT);
+ com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+ com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+ infoBytes);
+ add(inferenceInfo);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+ }
+ }
+
+ public void addInferenceInfoFromBundle(Bundle b) {
+ if (!b.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+ return;
+ }
+
+ try {
+ byte[] infoBytes = b.getByteArray(
+ OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+ if (infoBytes != null) {
+ com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+ com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+ infoBytes);
+ add(inferenceInfo);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+ }
+ }
+
+ private synchronized void add(com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+ while (!inferenceInfos.isEmpty()
+ && System.currentTimeMillis() - inferenceInfos.first().getStartTimeMillis()
+ > maxAgeMs) {
+ inferenceInfos.pollFirst();
+ }
+ inferenceInfos.add(toInferenceInfo(info));
+ }
+
+ private static InferenceInfo toInferenceInfo(
+ com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+ return new InferenceInfo.Builder(info.uid).setStartTimeMillis(
+ info.startTimeMs).setEndTimeMillis(info.endTimeMs).setSuspendedTimeMillis(
+ info.suspendedTimeMs).build();
+ }
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
new file mode 100644
index 000000000000..6badc53ae97e
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+
+/**
+ * Exposes APIs to {@code system_server} components outside of the module boundaries.
+ * <p> This API should be access using {@link com.android.server.LocalManagerRegistry}. </p>
+ *
+ * @hide
+ */
+@SystemApi(client = Client.SYSTEM_SERVER)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+public interface OnDeviceIntelligenceManagerLocal {
+ /**
+ * Gets the uid for the process that is currently hosting the
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
+ * the device.
+ */
+ int getInferenceServiceUid();
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
new file mode 100644
index 000000000000..a078f7542c11
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -0,0 +1,1147 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
+
+import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
+import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
+import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
+import static com.android.server.ondeviceintelligence.BundleUtil.wrapWithValidation;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.ICancellationSignal;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IRemoteCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.utils.BinderUtils;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
+import android.service.ondeviceintelligence.IRemoteProcessingService;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.modules.utils.AndroidFuture;
+import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.ServiceConnector;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.SystemService;
+import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is the system service for handling calls on the
+ * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}. This
+ * service holds connection references to the underlying remote services i.e. the isolated service
+ * {@link OnDeviceSandboxedInferenceService} and a regular
+ * service counter part {@link OnDeviceIntelligenceService}.
+ *
+ * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of
+ * the Inference service for each user, due to possible high memory footprint.
+ *
+ * @hide
+ */
+public class OnDeviceIntelligenceManagerService extends SystemService {
+
+ private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName();
+ private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+ /** Handler message to {@link #resetTemporaryServices()} */
+ private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+ /** Handler message to clean up temporary broadcast keys. */
+ private static final int MSG_RESET_BROADCAST_KEYS = 1;
+ /** Handler message to clean up temporary config namespace. */
+ private static final int MSG_RESET_CONFIG_NAMESPACE = 2;
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+ private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
+
+ private static final String SYSTEM_PACKAGE = "android";
+ private static final long MAX_AGE_MS = TimeUnit.HOURS.toMillis(3);
+
+
+ private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
+ private final Executor callbackExecutor = Executors.newCachedThreadPool();
+ private final Executor broadcastExecutor = Executors.newCachedThreadPool();
+ private final Executor mConfigExecutor = Executors.newCachedThreadPool();
+
+
+ private final Context mContext;
+ protected final Object mLock = new Object();
+
+ private final InferenceInfoStore mInferenceInfoStore;
+ private RemoteOnDeviceSandboxedInferenceService mRemoteInferenceService;
+ private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
+ volatile boolean mIsServiceEnabled;
+
+ @GuardedBy("mLock")
+ private int remoteInferenceServiceUid = -1;
+
+ @GuardedBy("mLock")
+ private String[] mTemporaryServiceNames;
+ @GuardedBy("mLock")
+ private String[] mTemporaryBroadcastKeys;
+ @GuardedBy("mLock")
+ private String mBroadcastPackageName = SYSTEM_PACKAGE;
+ @GuardedBy("mLock")
+ private String mTemporaryConfigNamespace;
+
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ this::sendUpdatedConfig;
+
+
+ /**
+ * Handler used to reset the temporary service names.
+ */
+ private Handler mTemporaryHandler;
+ private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+
+ public OnDeviceIntelligenceManagerService(Context context) {
+ super(context);
+ mContext = context;
+ mTemporaryServiceNames = new String[0];
+ mInferenceInfoStore = new InferenceInfoStore(MAX_AGE_MS);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(
+ Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(),
+ /* allowIsolated = */ true);
+ LocalManagerRegistry.addManager(OnDeviceIntelligenceManagerLocal.class,
+ this::getRemoteInferenceServiceUid);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ BackgroundThread.getExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ if (keys.contains(KEY_SERVICE_ENABLED)) {
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private boolean isServiceEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+
+ private IBinder getOnDeviceIntelligenceManagerService() {
+ return new IOnDeviceIntelligenceManager.Stub() {
+ @Override
+ public String getRemoteServicePackageName() {
+ return OnDeviceIntelligenceManagerService.this.getRemoteConfiguredPackageName();
+ }
+
+ @Override
+ public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.DUMP, TAG);
+ return OnDeviceIntelligenceManagerService.this.getLatestInferenceInfo(
+ startTimeEpochMillis);
+ }
+
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
+ Objects.requireNonNull(remoteCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ remoteCallback.sendResult(null);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getVersion(new RemoteCallback(
+ result -> {
+ remoteCallback.sendResult(result);
+ future.complete(null);
+ }));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ }
+
+ @Override
+ public void getFeature(int id, IFeatureCallback featureCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(featureCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureCallback.onFailure(
+ OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeature(callerUid, id, new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) throws RemoteException {
+ featureCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ featureCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ }
+
+ @Override
+ public void listFeatures(IListFeaturesCallback listFeaturesCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(listFeaturesCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ listFeaturesCallback.onFailure(
+ OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.listFeatures(callerUid,
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result)
+ throws RemoteException {
+ listFeaturesCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ listFeaturesCallback.onFailure(errorCode, errorMessage,
+ errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ }
+
+ @Override
+ public void getFeatureDetails(Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureDetailsCallback.onFailure(
+ OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeatureDetails(callerUid, feature,
+ new IFeatureDetailsCallback.Stub() {
+ @Override
+ public void onSuccess(FeatureDetails result)
+ throws RemoteException {
+ future.complete(null);
+ featureDetailsCallback.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ future.completeExceptionally(null);
+ featureDetailsCallback.onFailure(errorCode,
+ errorMessage, errorParams);
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ }
+
+ @Override
+ public void requestFeatureDownload(Feature feature,
+ AndroidFuture cancellationSignalFuture,
+ IDownloadCallback downloadCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ downloadCallback.onDownloadFailed(
+ DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ }
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ ListenableDownloadCallback listenableDownloadCallback =
+ new ListenableDownloadCallback(
+ downloadCallback,
+ mMainHandler, future, getIdleTimeoutMs());
+ service.requestFeatureDownload(callerUid, feature,
+ wrapCancellationFuture(cancellationSignalFuture),
+ listenableDownloadCallback);
+ return future; // this future has no timeout because, actual download
+ // might take long, but we fail early if there is no progress callbacks.
+ }
+ );
+ }
+
+
+ @Override
+ public void requestTokenInfo(Feature feature,
+ Bundle request,
+ AndroidFuture cancellationSignalFuture,
+ ITokenInfoCallback tokenInfoCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestTokenInfo");
+ AndroidFuture<Void> result = null;
+ try {
+ Objects.requireNonNull(feature);
+ sanitizeInferenceParams(request);
+ Objects.requireNonNull(tokenInfoCallback);
+
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ tokenInfoCallback.onFailure(
+ OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ }
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.requestTokenInfo(callerUid, feature,
+ request,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapWithValidation(tokenInfoCallback, future,
+ mInferenceInfoStore));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
+ resourceClosingExecutor);
+ } finally {
+ if (result == null) {
+ resourceClosingExecutor.execute(() -> BundleUtil.tryCloseResource(request));
+ }
+ }
+ }
+
+ @Override
+ public void processRequest(Feature feature,
+ Bundle request,
+ int requestType,
+ AndroidFuture cancellationSignalFuture,
+ AndroidFuture processingSignalFuture,
+ IResponseCallback responseCallback)
+ throws RemoteException {
+ AndroidFuture<Void> result = null;
+ try {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequest");
+ Objects.requireNonNull(feature);
+ sanitizeInferenceParams(request);
+ Objects.requireNonNull(responseCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ responseCallback.onFailure(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ }
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequest(callerUid, feature,
+ request,
+ requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(responseCallback,
+ resourceClosingExecutor, future,
+ mInferenceInfoStore));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
+ resourceClosingExecutor);
+ } finally {
+ if (result == null) {
+ resourceClosingExecutor.execute(() -> BundleUtil.tryCloseResource(request));
+ }
+ }
+ }
+
+ @Override
+ public void processRequestStreaming(Feature feature,
+ Bundle request,
+ int requestType,
+ AndroidFuture cancellationSignalFuture,
+ AndroidFuture processingSignalFuture,
+ IStreamingResponseCallback streamingCallback) throws RemoteException {
+ AndroidFuture<Void> result = null;
+ try {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequestStreaming");
+ Objects.requireNonNull(feature);
+ sanitizeInferenceParams(request);
+ Objects.requireNonNull(streamingCallback);
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ streamingCallback.onFailure(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ PersistableBundle.EMPTY);
+ }
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ int callerUid = Binder.getCallingUid();
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequestStreaming(callerUid,
+ feature,
+ request, requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(streamingCallback,
+ resourceClosingExecutor, future,
+ mInferenceInfoStore));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
+ result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
+ resourceClosingExecutor);
+ } finally {
+ if (result == null) {
+ resourceClosingExecutor.execute(() -> BundleUtil.tryCloseResource(request));
+ }
+ }
+ }
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return new com.android.server.ondeviceintelligence.OnDeviceIntelligenceShellCommand(
+ OnDeviceIntelligenceManagerService.this).exec(
+ this,
+ in.getFileDescriptor(),
+ out.getFileDescriptor(),
+ err.getFileDescriptor(),
+ args);
+ }
+ };
+ }
+
+ private boolean ensureRemoteIntelligenceServiceInitialized(boolean throwIfServiceInvalid) {
+ synchronized (mLock) {
+ if (mRemoteOnDeviceIntelligenceService == null) {
+ String serviceName = getServiceNames()[0];
+ if (!BinderUtils.withCleanCallingIdentity(
+ () -> validateServiceElevated(serviceName, false,
+ throwIfServiceInvalid))) {
+ return false;
+ }
+ mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(
+ mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ mRemoteOnDeviceIntelligenceService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onConnected(
+ @NonNull IOnDeviceIntelligenceService service) {
+ try {
+ service.registerRemoteServices(
+ getRemoteProcessingService());
+ service.ready();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send connected event", ex);
+ }
+ }
+ });
+ }
+ }
+ return true;
+ }
+
+ @NonNull
+ private IRemoteProcessingService.Stub getRemoteProcessingService() {
+ return new IRemoteProcessingService.Stub() {
+ @Override
+ public void updateProcessingState(
+ Bundle processingState,
+ IProcessingUpdateStatusCallback callback) {
+ callbackExecutor.execute(() -> {
+ AndroidFuture<Void> result = null;
+ try {
+ sanitizeStateParams(processingState);
+ if (ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */
+ false)) {
+ result = mRemoteInferenceService.post(
+ service -> service.updateProcessingState(
+ processingState, callback));
+ result.whenCompleteAsync(
+ (c, e) -> BundleUtil.tryCloseResource(processingState),
+ resourceClosingExecutor);
+ } else {
+ callback.onFailure(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "Remote service cannot be initialized.");
+ }
+ } catch (RemoteException e) {
+ Slog.w("Failed to invoke updateProcessingState", e);
+ } finally {
+ if (result == null) {
+ resourceClosingExecutor.execute(
+ () -> BundleUtil.tryCloseResource(processingState));
+ }
+ }
+ });
+ }
+ };
+ }
+
+ private boolean ensureRemoteInferenceServiceInitialized(boolean throwIfServiceInvalid) {
+ synchronized (mLock) {
+ if (mRemoteInferenceService == null) {
+ String serviceName = getServiceNames()[1];
+ if (!BinderUtils.withCleanCallingIdentity(
+ () -> validateServiceElevated(serviceName, true, throwIfServiceInvalid))) {
+ return false;
+ }
+ mRemoteInferenceService = new RemoteOnDeviceSandboxedInferenceService(mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ mRemoteInferenceService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onConnected(
+ @NonNull IOnDeviceSandboxedInferenceService service) {
+ try {
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
+ service.registerRemoteStorageService(
+ getIRemoteStorageService(), new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle bundle) {
+ final int uid = Binder.getCallingUid();
+ setRemoteInferenceServiceUid(uid);
+ }
+ });
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
+ broadcastExecutor.execute(
+ () -> registerModelLoadingBroadcasts(service));
+ mConfigExecutor.execute(
+ () -> registerDeviceConfigChangeListener());
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send connected event", ex);
+ }
+ }
+
+ @Override
+ public void onDisconnected(
+ @NonNull IOnDeviceSandboxedInferenceService service) {
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
+ }
+
+ @Override
+ public void onBinderDied() {
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
+ }
+ });
+ }
+ }
+ return true;
+ }
+
+ private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
+ String[] modelBroadcastKeys;
+ try {
+ modelBroadcastKeys = getBroadcastKeys();
+ } catch (Resources.NotFoundException e) {
+ Slog.d(TAG, "Skipping model broadcasts as broadcast intents configured.");
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY, true);
+ try {
+ service.updateProcessingState(bundle, new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle statusParams) {
+ Binder.clearCallingIdentity();
+ synchronized (mLock) {
+ if (statusParams.containsKey(MODEL_LOADED_BUNDLE_KEY)) {
+ String modelLoadedBroadcastKey = modelBroadcastKeys[0];
+ if (modelLoadedBroadcastKey != null
+ && !modelLoadedBroadcastKey.isEmpty()) {
+ final Intent intent = new Intent(modelLoadedBroadcastKey);
+ intent.setPackage(mBroadcastPackageName);
+ mContext.sendBroadcast(intent,
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+ }
+ } else if (statusParams.containsKey(MODEL_UNLOADED_BUNDLE_KEY)) {
+ String modelUnloadedBroadcastKey = modelBroadcastKeys[1];
+ if (modelUnloadedBroadcastKey != null
+ && !modelUnloadedBroadcastKey.isEmpty()) {
+ final Intent intent = new Intent(modelUnloadedBroadcastKey);
+ intent.setPackage(mBroadcastPackageName);
+ mContext.sendBroadcast(intent,
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ Slog.e(TAG, "Failed to register model loading callback with status code",
+ new OnDeviceIntelligenceException(errorCode, errorMessage));
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register model loading callback with status code", e);
+ }
+ }
+
+ private void registerDeviceConfigChangeListener() {
+ Log.d(TAG, "registerDeviceConfigChangeListener");
+ String configNamespace = getConfigNamespace();
+ if (configNamespace.isEmpty()) {
+ Slog.e(TAG, "config_defaultOnDeviceIntelligenceDeviceConfigNamespace is empty");
+ return;
+ }
+ DeviceConfig.addOnPropertiesChangedListener(
+ configNamespace,
+ mConfigExecutor,
+ mOnPropertiesChangedListener);
+ }
+
+ private String getConfigNamespace() {
+ synchronized (mLock) {
+ if (mTemporaryConfigNamespace != null) {
+ return mTemporaryConfigNamespace;
+ }
+ return mContext.getResources().getString(
+ android.R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace);
+ }
+ }
+
+ private void sendUpdatedConfig(
+ DeviceConfig.Properties props) {
+ Log.d(TAG, "sendUpdatedConfig");
+
+ PersistableBundle persistableBundle = new PersistableBundle();
+ for (String key : props.getKeyset()) {
+ persistableBundle.putString(key, props.getString(key, ""));
+ }
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(DEVICE_CONFIG_UPDATE_BUNDLE_KEY, persistableBundle);
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
+ mRemoteInferenceService.run(service -> service.updateProcessingState(bundle,
+ new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle result) {
+ Slog.d(TAG, "Config update successful." + result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ Slog.e(TAG, "Config update failed with code ["
+ + String.valueOf(errorCode) + "] and message = " + errorMessage);
+ }
+ }));
+ }
+
+ @NonNull
+ private IRemoteStorageService.Stub getIRemoteStorageService() {
+ return new IRemoteStorageService.Stub() {
+ @Override
+ public void getReadOnlyFileDescriptor(
+ String filePath,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ future.completeExceptionally(new OnDeviceIntelligenceException(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_NOT_AVAILABLE));
+ return;
+ }
+ AndroidFuture<ParcelFileDescriptor> pfdFuture = new AndroidFuture<>();
+ mRemoteOnDeviceIntelligenceService.run(
+ service -> service.getReadOnlyFileDescriptor(
+ filePath, pfdFuture));
+ pfdFuture.whenCompleteAsync((pfd, error) -> {
+ try {
+ if (error != null) {
+ future.completeExceptionally(error);
+ } else {
+ validatePfdReadOnly(pfd);
+ future.complete(pfd);
+ }
+ } finally {
+ tryClosePfd(pfd);
+ }
+ }, callbackExecutor);
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature,
+ RemoteCallback remoteCallback) {
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+ mRemoteOnDeviceIntelligenceService.run(
+ service -> service.getReadOnlyFeatureFileDescriptorMap(
+ feature,
+ new RemoteCallback(result -> callbackExecutor.execute(() -> {
+ try {
+ if (result == null) {
+ remoteCallback.sendResult(null);
+ }
+ for (String key : result.keySet()) {
+ ParcelFileDescriptor pfd = result.getParcelable(key,
+ ParcelFileDescriptor.class);
+ validatePfdReadOnly(pfd);
+ }
+ remoteCallback.sendResult(result);
+ } finally {
+ resourceClosingExecutor.execute(
+ () -> BundleUtil.tryCloseResource(result));
+ }
+ }))));
+ }
+ };
+ }
+
+ private boolean validateServiceElevated(String serviceName, boolean checkIsolated,
+ boolean throwIfServiceInvalid) {
+ try {
+ if (TextUtils.isEmpty(serviceName)) {
+ if (throwIfServiceInvalid) {
+ throw new IllegalStateException(
+ "Remote service is not configured to complete the request");
+ }
+ return false;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(
+ serviceName);
+ ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(
+ serviceComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ if (!checkIsolated) {
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+ return true;
+ }
+
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE);
+ if (!isIsolatedService(serviceInfo)) {
+ throw new SecurityException(
+ "Call required an isolated service, but the configured service: "
+ + serviceName + ", is not isolated");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ if (throwIfServiceInvalid) {
+ throw new IllegalStateException(
+ "Remote service is not configured to complete the request.");
+ }
+ return false;
+ } catch (SecurityException e) {
+ if (throwIfServiceInvalid) {
+ throw e;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
+ String requiredPermission) {
+ final String permission = serviceInfo.permission;
+ if (!requiredPermission.equals(permission)) {
+ throw new SecurityException(String.format(
+ "%s requires %s permission. Found %s permission",
+ serviceInfo,
+ requiredPermission,
+ serviceInfo.permission));
+ }
+ }
+
+ private static boolean isIsolatedService(@NonNull ServiceInfo serviceInfo) {
+ return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
+ }
+
+ private List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ return mInferenceInfoStore.getLatestInferenceInfo(startTimeEpochMillis);
+ }
+
+ @Nullable
+ public String getRemoteConfiguredPackageName() {
+ try {
+ String[] serviceNames = getServiceNames();
+ ComponentName componentName = ComponentName.unflattenFromString(serviceNames[1]);
+ if (componentName != null) {
+ return componentName.getPackageName();
+ }
+ } catch (Resources.NotFoundException e) {
+ Slog.e(TAG, "Could not find resource", e);
+ }
+
+ return null;
+ }
+
+
+ protected String[] getServiceNames() throws Resources.NotFoundException {
+ // TODO 329240495 : Consider a small class with explicit field names for the two services
+ synchronized (mLock) {
+ if (mTemporaryServiceNames != null && mTemporaryServiceNames.length == 2) {
+ return mTemporaryServiceNames;
+ }
+ }
+ return new String[]{mContext.getResources().getString(
+ android.R.string.config_defaultOnDeviceIntelligenceService),
+ mContext.getResources().getString(
+ android.R.string.config_defaultOnDeviceSandboxedInferenceService)};
+ }
+
+ protected String[] getBroadcastKeys() throws Resources.NotFoundException {
+ // TODO 329240495 : Consider a small class with explicit field names for the two services
+ synchronized (mLock) {
+ if (mTemporaryBroadcastKeys != null && mTemporaryBroadcastKeys.length == 2) {
+ return mTemporaryBroadcastKeys;
+ }
+ }
+
+ return new String[]{MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT};
+ }
+
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void setTemporaryServices(@NonNull String[] componentNames, int durationMs) {
+ Objects.requireNonNull(componentNames);
+ enforceShellOnly(Binder.getCallingUid(), "setTemporaryServices");
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ synchronized (mLock) {
+ mTemporaryServiceNames = componentNames;
+ if (mRemoteInferenceService != null) {
+ mRemoteInferenceService.unbind();
+ mRemoteInferenceService = null;
+ }
+ if (mRemoteOnDeviceIntelligenceService != null) {
+ mRemoteOnDeviceIntelligenceService.unbind();
+ mRemoteOnDeviceIntelligenceService = null;
+ }
+
+ if (durationMs != -1) {
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE,
+ durationMs);
+ }
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void setModelBroadcastKeys(@NonNull String[] broadcastKeys, String receiverPackageName,
+ int durationMs) {
+ Objects.requireNonNull(broadcastKeys);
+ enforceShellOnly(Binder.getCallingUid(), "setModelBroadcastKeys");
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ synchronized (mLock) {
+ mTemporaryBroadcastKeys = broadcastKeys;
+ mBroadcastPackageName = receiverPackageName;
+ if (durationMs != -1) {
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_BROADCAST_KEYS, durationMs);
+ }
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void setTemporaryDeviceConfigNamespace(@NonNull String configNamespace,
+ int durationMs) {
+ Objects.requireNonNull(configNamespace);
+ enforceShellOnly(Binder.getCallingUid(), "setTemporaryDeviceConfigNamespace");
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ synchronized (mLock) {
+ mTemporaryConfigNamespace = configNamespace;
+ if (durationMs != -1) {
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_CONFIG_NAMESPACE,
+ durationMs);
+ }
+ }
+ }
+
+ /**
+ * Reset the temporary services set in CTS tests, this method is primarily used to only revert
+ * the changes caused by CTS tests.
+ */
+ public void resetTemporaryServices() {
+ synchronized (mLock) {
+ if (mTemporaryHandler != null) {
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ mTemporaryHandler = null;
+ }
+
+ mRemoteInferenceService = null;
+ mRemoteOnDeviceIntelligenceService = null;
+ mTemporaryServiceNames = new String[0];
+ }
+ }
+
+ /**
+ * Throws if the caller is not of a shell (or root) UID.
+ *
+ * @param callingUid pass Binder.callingUid().
+ */
+ public static void enforceShellOnly(int callingUid, String message) {
+ if (callingUid == android.os.Process.SHELL_UID
+ || callingUid == android.os.Process.ROOT_UID) {
+ return; // okay
+ }
+
+ throw new SecurityException(message + ": Only shell user can call it");
+ }
+
+ private AndroidFuture<IBinder> wrapCancellationFuture(
+ AndroidFuture future) {
+ if (future == null) {
+ return null;
+ }
+ AndroidFuture<IBinder> cancellationFuture = new AndroidFuture<>();
+ cancellationFuture.whenCompleteAsync((c, e) -> {
+ if (e != null) {
+ Log.e(TAG, "Error forwarding ICancellationSignal to manager layer", e);
+ future.completeExceptionally(e);
+ } else {
+ future.complete(new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ ICancellationSignal.Stub.asInterface(c).cancel();
+ }
+ });
+ }
+ });
+ return cancellationFuture;
+ }
+
+ private AndroidFuture<IBinder> wrapProcessingFuture(
+ AndroidFuture future) {
+ if (future == null) {
+ return null;
+ }
+ AndroidFuture<IBinder> processingSignalFuture = new AndroidFuture<>();
+ processingSignalFuture.whenCompleteAsync((c, e) -> {
+ if (e != null) {
+ future.completeExceptionally(e);
+ } else {
+ future.complete(new IProcessingSignal.Stub() {
+ @Override
+ public void sendSignal(PersistableBundle actionParams) throws RemoteException {
+ IProcessingSignal.Stub.asInterface(c).sendSignal(actionParams);
+ }
+ });
+ }
+ });
+ return processingSignalFuture;
+ }
+
+ private static void tryClosePfd(ParcelFileDescriptor pfd) {
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close parcel file descriptor ", e);
+ }
+ }
+ }
+
+ private synchronized Handler getTemporaryHandler() {
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ resetTemporaryServices();
+ } else if (msg.what == MSG_RESET_BROADCAST_KEYS) {
+ mTemporaryBroadcastKeys = null;
+ mBroadcastPackageName = SYSTEM_PACKAGE;
+ } else if (msg.what == MSG_RESET_CONFIG_NAMESPACE) {
+ mTemporaryConfigNamespace = null;
+ } else {
+ Slog.wtf(TAG, "invalid handler msg: " + msg);
+ }
+ }
+ }
+ };
+ }
+
+ return mTemporaryHandler;
+ }
+
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
+ @SuppressWarnings("NonUserGetterCalled")
+ private long getIdleTimeoutMs() {
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
+ TimeUnit.HOURS.toMillis(1));
+ }
+
+ private int getRemoteInferenceServiceUid() {
+ synchronized (mLock) {
+ return remoteInferenceServiceUid;
+ }
+ }
+
+ private void setRemoteInferenceServiceUid(int remoteInferenceServiceUid) {
+ synchronized (mLock) {
+ this.remoteInferenceServiceUid = remoteInferenceServiceUid;
+ }
+ }
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
new file mode 100644
index 000000000000..c641de8b47b1
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+final class OnDeviceIntelligenceShellCommand extends BasicShellCommandHandler {
+ private static final String TAG = OnDeviceIntelligenceShellCommand.class.getSimpleName();
+
+ @NonNull
+ private final OnDeviceIntelligenceManagerService mService;
+
+ OnDeviceIntelligenceShellCommand(@NonNull OnDeviceIntelligenceManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case "set-temporary-services":
+ return setTemporaryServices();
+ case "get-services":
+ return getConfiguredServices();
+ case "set-model-broadcasts":
+ return setBroadcastKeys();
+ case "set-deviceconfig-namespace":
+ return setDeviceConfigNamespace();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("OnDeviceIntelligenceShellCommand commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(
+ " set-temporary-services [IntelligenceServiceComponentName] "
+ + "[InferenceServiceComponentName] [DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implementations.");
+ pw.println(" To reset, call without any arguments.");
+
+ pw.println(" get-services To get the names of services that are currently being used.");
+ pw.println(
+ " set-model-broadcasts [ModelLoadedBroadcastKey] [ModelUnloadedBroadcastKey] "
+ + "[ReceiverPackageName] "
+ + "[DURATION] To set the names of broadcast intent keys that are to be "
+ + "emitted for cts tests.");
+ pw.println(
+ " set-deviceconfig-namespace [DeviceConfigNamespace] "
+ + "[DURATION] To set the device config namespace "
+ + "to use for cts tests.");
+ }
+
+ private int setTemporaryServices() {
+ final PrintWriter out = getOutPrintWriter();
+ final String intelligenceServiceName = getNextArg();
+ final String inferenceServiceName = getNextArg();
+
+ if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
+ && inferenceServiceName == null) {
+ OnDeviceIntelligenceManagerService.enforceShellOnly(Binder.getCallingUid(),
+ "resetTemporaryServices");
+ mService.resetTemporaryServices();
+ out.println("OnDeviceIntelligenceManagerService temporary reset. ");
+ return 0;
+ }
+
+ Objects.requireNonNull(intelligenceServiceName);
+ Objects.requireNonNull(inferenceServiceName);
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryServices(
+ new String[]{intelligenceServiceName, inferenceServiceName},
+ duration);
+ out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
+ + " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
+ + " for " + duration + "ms");
+ return 0;
+ }
+
+ private int getConfiguredServices() {
+ final PrintWriter out = getOutPrintWriter();
+ String[] services = mService.getServiceNames();
+ out.println("OnDeviceIntelligenceService set to : " + services[0]
+ + " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
+ return 0;
+ }
+
+ private int setBroadcastKeys() {
+ final PrintWriter out = getOutPrintWriter();
+ final String modelLoadedKey = getNextArgRequired();
+ final String modelUnloadedKey = getNextArgRequired();
+ final String receiverPackageName = getNextArg();
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setModelBroadcastKeys(
+ new String[]{modelLoadedKey, modelUnloadedKey}, receiverPackageName, duration);
+ out.println("OnDeviceIntelligence Model Loading broadcast keys temporarily set to "
+ + modelLoadedKey
+ + " \n and \n OnDeviceTrustedInferenceService set to " + modelUnloadedKey
+ + "\n and Package name set to : " + receiverPackageName
+ + " for " + duration + "ms");
+ return 0;
+ }
+
+ private int setDeviceConfigNamespace() {
+ final PrintWriter out = getOutPrintWriter();
+ final String configNamespace = getNextArg();
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryDeviceConfigNamespace(configNamespace, duration);
+ out.println("OnDeviceIntelligence DeviceConfig Namespace temporarily set to "
+ + configNamespace
+ + " for " + duration + "ms");
+ return 0;
+ }
+
+} \ No newline at end of file
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
new file mode 100644
index 000000000000..0c43a309c456
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+
+import com.android.modules.utils.ServiceConnector;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
+ * logic set by the service implementation via a Secure Settings flag.
+ *
+ * @hide
+ */
+public class RemoteOnDeviceIntelligenceService extends
+ ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
+ private static final String TAG =
+ RemoteOnDeviceIntelligenceService.class.getSimpleName();
+
+ RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceIntelligenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
+ @Override
+ @SuppressWarnings("NonUserGetterCalled")
+ protected long getAutoDisconnectTimeoutMs() {
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30));
+ }
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
new file mode 100644
index 000000000000..8c5d5a7ba736
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -0,0 +1,80 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+
+import com.android.modules.utils.ServiceConnector;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
+ * logic set by the service implementation via a SecureSettings flag.
+ *
+ * @hide
+ */
+public class RemoteOnDeviceSandboxedInferenceService extends
+ ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
+ /**
+ * Creates an instance of {@link ServiceConnector}
+ *
+ * See {@code protected} methods for optional parameters you can override.
+ *
+ * @param context to be used for {@link Context#bindServiceAsUser binding} and
+ * {@link Context#unbindService unbinding}
+ * @param userId to be used for {@link Context#bindServiceAsUser binding}
+ */
+ RemoteOnDeviceSandboxedInferenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceSandboxedInferenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceSandboxedInferenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
+ @Override
+ @SuppressWarnings("NonUserGetterCalled")
+ protected long getAutoDisconnectTimeoutMs() {
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30));
+ }
+}
diff --git a/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 000000000000..249bcd37db5d
--- /dev/null
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.modules.utils.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ *
+ * @hide
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+ private final IDownloadCallback callback;
+ private final Handler handler;
+ private final AndroidFuture future;
+ private final long idleTimeoutMs;
+
+ /**
+ * Constructor to create a ListenableDownloadCallback.
+ *
+ * @param callback callback to send download updates to caller.
+ * @param handler handler to schedule timeout runnable.
+ * @param future future to complete to signal the callback has reached a terminal state.
+ * @param idleTimeoutMs timeout within which download updates should be received.
+ */
+ public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+ AndroidFuture future,
+ long idleTimeoutMs) {
+ this.callback = callback;
+ this.handler = handler;
+ this.future = future;
+ this.idleTimeoutMs = idleTimeoutMs;
+ handler.postDelayed(this,
+ idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+ }
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+ callback.onDownloadStarted(bytesToDownload);
+ handler.removeCallbacks(this);
+ handler.postDelayed(this, idleTimeoutMs);
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+ callback.onDownloadProgress(bytesDownloaded);
+ handler.removeCallbacks(this); // remove previously queued timeout tasks.
+ handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, PersistableBundle errorParams) throws RemoteException {
+ callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ handler.removeCallbacks(this);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDownloadCompleted(
+ android.os.PersistableBundle downloadParams) throws RemoteException {
+ callback.onDownloadCompleted(downloadParams);
+ handler.removeCallbacks(this);
+ future.complete(null);
+ }
+
+ @Override
+ public void run() {
+ future.completeExceptionally(
+ new TimeoutException()); // complete the future as we haven't received updates
+ // for download progress.
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 7dd8f2fdcecb..7dd8f2fdcecb 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java
diff --git a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
index bef3f8048da1..bef3f8048da1 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
new file mode 100644
index 000000000000..6badc53ae97e
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+
+/**
+ * Exposes APIs to {@code system_server} components outside of the module boundaries.
+ * <p> This API should be access using {@link com.android.server.LocalManagerRegistry}. </p>
+ *
+ * @hide
+ */
+@SystemApi(client = Client.SYSTEM_SERVER)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+public interface OnDeviceIntelligenceManagerLocal {
+ /**
+ * Gets the uid for the process that is currently hosting the
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
+ * the device.
+ */
+ int getInferenceServiceUid();
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index b0d69e67dac5..0a69af67a8bf 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -87,7 +87,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.os.BackgroundThread;
-import com.android.server.LocalServices;
+import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
@@ -182,9 +182,9 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
public void onStart() {
publishBinderService(
Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(),
- /* allowIsolated = */true);
- LocalServices.addService(OnDeviceIntelligenceManagerInternal.class,
- this::getRemoteInferenceServiceUid);
+ /* allowIsolated = */ true);
+ LocalManagerRegistry.addManager(OnDeviceIntelligenceManagerLocal.class,
+ this::getRemoteInferenceServiceUid);
}
@Override
@@ -199,20 +199,6 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
}
- @Override
- public void onUserUnlocked(@NonNull TargetUser user) {
- Slog.d(TAG, "onUserUnlocked: " + user.getUserHandle());
- //connect to remote services(if available) during boot.
- if(user.getUserHandle().equals(UserHandle.SYSTEM)) {
- try {
- ensureRemoteInferenceServiceInitialized();
- ensureRemoteIntelligenceServiceInitialized();
- } catch (Exception e) {
- Slog.w(TAG, "Couldn't pre-start remote ondeviceintelligence services.", e);
- }
- }
- }
-
private void onDeviceConfigChange(@NonNull Set<String> keys) {
if (keys.contains(KEY_SERVICE_ENABLED)) {
mIsServiceEnabled = isServiceEnabled();
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
index d2c84fa1b18a..d2c84fa1b18a 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index ac9747aa83b3..ac9747aa83b3 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 18b13838ea7c..18b13838ea7c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
index 32f0698a8f9c..32f0698a8f9c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 50db5018d44e..50331014f926 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -45,6 +45,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"options":[
{
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 77fad551eba7..ac96d7adf48d 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -24,8 +24,8 @@
<string name="installing" msgid="4921993079741206516">"Installeer tans …"</string>
<string name="installing_app" msgid="1165095864863849422">"Installeer tans <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
<string name="install_done" msgid="5987363587661783896">"App geïnstalleer."</string>
- <string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie program installeer?"</string>
- <string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie program opdateer?"</string>
+ <string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie app installeer?"</string>
+ <string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie app opdateer?"</string>
<string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou tablet kry. Appfunksies kan verander.&lt;/p&gt;"</string>
<string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou TV kry. Appfunksies kan verander.&lt;/p&gt;"</string>
<string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou foon kry. Appfunksies kan verander.&lt;/p&gt;"</string>
@@ -66,9 +66,9 @@
<string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil jy hierdie app wat in jou privaat ruimte is, argiveer? Jou persoonlike data sal gestoor word"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil jy hierdie app vir "<b>"alle"</b>" gebruikers deïnstalleer? Die app en sy data sal van "<b>"alle"</b>" gebruikers op hierdie toestel verwyder word."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Wil jy hierdie app vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
- <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie program op jou werkprofiel deïnstalleer?"</string>
+ <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie app op jou werkprofiel deïnstalleer?"</string>
<string name="uninstall_update_text" msgid="863648314632448705">"Vervang hierdie app met die fabriekweergawe? Alle data sal verwyder word."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vervang hierdie app met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."</string>
<string name="uninstall_keep_data" msgid="7002379587465487550">"Hou <xliff:g id="SIZE">%1$s</xliff:g> se programdata."</string>
<string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Wil jy hierdie app uitvee?"</string>
<string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Wil jy hierdie app deïnstalleer? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon sal ook uitgevee word."</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 379dfe32cc51..635ae20cfa38 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -36,6 +36,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -62,9 +63,12 @@ public class InstallStart extends Activity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ boolean testOverrideForPiaV2 = Settings.System.getInt(getContentResolver(),
+ "use_pia_v2", 0) == 1;
+ boolean usePiaV2aConfig = usePiaV2();
- if (usePiaV2()) {
- Log.i(TAG, "Using Pia V2");
+ if (usePiaV2aConfig || testOverrideForPiaV2) {
+ logReasonForDebug(usePiaV2aConfig, testOverrideForPiaV2);
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getLaunchedFromPackage());
@@ -381,4 +385,20 @@ public class InstallStart extends Activity {
}
return null;
}
+
+ private void logReasonForDebug(boolean usePiaV2aConfig, boolean testOverrideForPiaV2) {
+ StringBuilder sb = new StringBuilder("Using Pia V2 due to: ");
+ boolean aconfigUsed = false;
+ if (usePiaV2aConfig) {
+ sb.append("aconfig flag USE_PIA_V2");
+ aconfigUsed = true;
+ }
+ if (testOverrideForPiaV2) {
+ if (aconfigUsed) {
+ sb.append(" and ");
+ }
+ sb.append("testOverrideForPiaV2.");
+ }
+ Log.i(TAG, sb.toString());
+ }
}
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 8d6f0da4262f..fdc59754df95 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="0dp"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 307277264ff6..405c45272792 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
<corners
android:topLeftRadius="0dp"
android:topRightRadius="?android:attr/dialogCornerRadius"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index f1790f9ba351..187e612d11c4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
<corners
android:radius="?android:attr/dialogCornerRadius"
/>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
index d56c8434824f..0182b4c650a1 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
@@ -18,7 +18,7 @@
<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:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
<corners
android:radius="0dp"
/>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
index cc948a670382..fd8cecb8536e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
@@ -22,12 +22,13 @@
<item name="iconGravity">textTop</item>
</style>
- <style name="SettingsLibActionButton.Expressive.Label" parent="SettingsLibTextAppearance.Emphasized.Title.Small">
+ <style name="SettingsLibActionButton.Expressive.Label" parent="">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:minWidth">@dimen/settingslib_expressive_space_small3</item>
<item name="android:minHeight">@dimen/settingslib_expressive_space_small3</item>
- <item name="android:textColor">@color/settingslib_materialColorOnSurface</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleSmall.Emphasized</item>
+ <item name="android:textColor">@color/settingslib_text_color_primary</item>
<item name="android:layout_gravity">center</item>
</style>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index a3da93da31ae..d739aafea929 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -65,6 +65,7 @@ android_library {
libs:[
// This flag library has been added in frameworks jar
"aconfig_settingslib_flags_java_lib",
+ "wifi_framework_aconfig_flags_lib",
],
plugins: ["androidx.room_room-compiler-plugin"],
use_resource_processor: true,
diff --git a/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
index 716ed412eb5c..9018baca79e7 100644
--- a/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
+++ b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
@@ -40,7 +40,6 @@
<ImageView
android:id="@android:id/icon"
- android:src="@drawable/settingslib_arrow_drop_down"
android:layout_width="@dimen/settingslib_expressive_space_medium3"
android:layout_height="@dimen/settingslib_expressive_space_medium3"
android:scaleType="centerInside"/>
@@ -60,16 +59,12 @@
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
android:textAppearance="@style/TextAppearance.CardTitle.SettingsLib"/>
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
android:textAppearance="@style/TextAppearance.CardSummary.SettingsLib"/>
</LinearLayout>
diff --git a/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
index 4cbdea52d439..287b13fa0d50 100644
--- a/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
@@ -17,14 +17,12 @@
<resources>
<style name="TextAppearance.CardTitle.SettingsLib"
- parent="@style/TextAppearance.PreferenceTitle.SettingsLib">
+ parent="@style/TextAppearance.SettingsLib.TitleMedium.Emphasized">
<item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
- <item name="android:textSize">20sp</item>
</style>
<style name="TextAppearance.CardSummary.SettingsLib"
- parent="@style/TextAppearance.PreferenceSummary.SettingsLib">
+ parent="@style/TextAppearance.SettingsLib.LabelMedium">
<item name="android:textColor">@color/settingslib_materialColorOnSecondary</item>
- <item name="android:textSize">14sp</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
index ccbe20e1c61f..9986a60250fe 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
@@ -17,12 +17,9 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:start="16dp"
- android:end="16dp"
- android:top="4dp"
- android:bottom="4dp">
+ android:start="16dp">
<shape>
- <size android:width="32dp" android:height="40dp" />
+ <size android:width="40dp" android:height="40dp" />
<solid android:color="@color/settingslib_materialColorSurfaceContainerHighest" />
<corners
android:radius="100dp" />
@@ -30,23 +27,21 @@
</item>
<item
- android:width="24dp"
- android:height="24dp"
+ android:width="16dp"
+ android:height="16dp"
android:gravity="center"
android:start="16dp"
- android:end="16dp"
- android:top="4dp"
- android:bottom="4dp">
+>
<vector
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="960"
- android:viewportHeight="960"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="16"
+ android:viewportHeight="16"
android:tint="@color/settingslib_materialColorOnSurfaceVariant"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
- android:pathData="M313,520L537,744L480,800L160,480L480,160L537,216L313,440L800,440L800,520L313,520Z"/>
+ android:pathData="M3.626,9L8.526,13.9C8.726,14.1 8.817,14.333 8.801,14.6C8.801,14.867 8.701,15.1 8.501,15.3C8.301,15.483 8.067,15.583 7.801,15.6C7.534,15.6 7.301,15.5 7.101,15.3L0.501,8.7C0.401,8.6 0.326,8.492 0.276,8.375C0.242,8.258 0.226,8.133 0.226,8C0.226,7.867 0.242,7.742 0.276,7.625C0.326,7.508 0.401,7.4 0.501,7.3L7.101,0.7C7.284,0.517 7.509,0.425 7.776,0.425C8.059,0.425 8.301,0.517 8.501,0.7C8.701,0.9 8.801,1.142 8.801,1.425C8.801,1.692 8.701,1.925 8.501,2.125L3.626,7H14.801C15.084,7 15.317,7.1 15.501,7.3C15.701,7.483 15.801,7.717 15.801,8C15.801,8.283 15.701,8.525 15.501,8.725C15.317,8.908 15.084,9 14.801,9H3.626Z"/>
</vector>
</item>
</layer-list> \ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
index e68253e2200d..fadcf7ba8699 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
@@ -18,7 +18,7 @@
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
- <item name="colorPrimary">@color/settingslib_materialColorInverseOnSurface</item>
+ <item name="colorPrimary">@color/settingslib_materialColorOnSurfaceInverse</item>
<item name="colorAccent">@color/settingslib_materialColorPrimaryFixed</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
index d58c2c2eeb23..37a78101cc4e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
@@ -33,12 +33,12 @@
<item name="contentScrim">@color/settingslib_materialColorSurfaceContainer</item>
</style>
- <style name="SettingsLibCollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <style name="SettingsLibCollapsingToolbarTitle.Collapsed" parent="@style/TextAppearance.SettingsLib.TitleLarge.Emphasized">
<!--set dp because we don't want size adjust when font size change-->
- <item name="android:textSize">20dp</item>
+ <item name="android:textSize">22dp</item>
</style>
- <style name="SettingsLibCollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
+ <style name="SettingsLibCollapsingToolbarTitle.Expanded" parent="@style/TextAppearance.SettingsLib.DisplaySmall.Emphasized">
<item name="android:textSize">36dp</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
index f7c9aac68629..7c9d1a47b7ef 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
@@ -18,7 +18,7 @@
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
- <item name="colorPrimary">@color/settingslib_materialColorInverseOnSurface</item>
+ <item name="colorPrimary">@color/settingslib_materialColorOnSurfaceInverse</item>
<item name="colorAccent">@color/settingslib_materialColorPrimary</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 5f1f8df02bbc..472ffa9289a7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -47,6 +47,37 @@ interface KeyValueStore : KeyedObservable<String> {
* @param value value to set, null means remove
*/
fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?)
+
+ /** Gets the boolean value of given key. */
+ fun getBoolean(key: String): Boolean? = getValue(key, Boolean::class.javaObjectType)
+
+ /** Sets boolean value for given key, null value means delete the key from data store. */
+ fun setBoolean(key: String, value: Boolean?) =
+ setValue(key, Boolean::class.javaObjectType, value)
+
+ /** Gets the float value of given key. */
+ fun getFloat(key: String): Float? = getValue(key, Float::class.javaObjectType)
+
+ /** Sets float value for given key, null value means delete the key from data store. */
+ fun setFloat(key: String, value: Float?) = setValue(key, Float::class.javaObjectType, value)
+
+ /** Gets the int value of given key. */
+ fun getInt(key: String): Int? = getValue(key, Int::class.javaObjectType)
+
+ /** Sets int value for given key, null value means delete the key from data store. */
+ fun setInt(key: String, value: Int?) = setValue(key, Int::class.javaObjectType, value)
+
+ /** Gets the long value of given key. */
+ fun getLong(key: String): Long? = getValue(key, Long::class.javaObjectType)
+
+ /** Sets long value for given key, null value means delete the key from data store. */
+ fun setLong(key: String, value: Long?) = setValue(key, Long::class.javaObjectType, value)
+
+ /** Gets the string value of given key. */
+ fun getString(key: String): String? = getValue(key, String::class.javaObjectType)
+
+ /** Sets string value for given key, null value means delete the key from data store. */
+ fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
}
/** [SharedPreferences] based [KeyValueStore]. */
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index cd03dd7ca1b3..07b1c9e3385e 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -22,6 +22,7 @@ import androidx.collection.MutableScatterMap
import com.google.errorprone.annotations.CanIgnoreReturnValue
import java.util.WeakHashMap
import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
/**
* Callback to be informed of changes in [KeyedObservable] object.
@@ -203,13 +204,71 @@ open class KeyedDataObservable<K> : KeyedObservable<K> {
}
}
- fun hasAnyObserver(): Boolean {
+ open fun hasAnyObserver(): Boolean {
synchronized(observers) { if (observers.isNotEmpty()) return true }
synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true }
return false
}
}
+/** [KeyedDataObservable] that maintains a counter for the observers. */
+abstract class AbstractKeyedDataObservable<K> : KeyedDataObservable<K>() {
+ /**
+ * Counter of observers.
+ *
+ * The value is accurate only when [addObserver] and [removeObserver] are invoked in pairs.
+ */
+ private val counter = AtomicInteger()
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) =
+ if (super.addObserver(observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) =
+ if (super.addObserver(key, observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverAdded() {
+ if (counter.getAndIncrement() == 0) onFirstObserverAdded()
+ }
+
+ /** Callbacks when the first observer is just added. */
+ protected abstract fun onFirstObserverAdded()
+
+ override fun removeObserver(observer: KeyedObserver<K?>) =
+ if (super.removeObserver(observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>) =
+ if (super.removeObserver(key, observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverRemoved() {
+ if (counter.decrementAndGet() == 0) onLastObserverRemoved()
+ }
+
+ /** Callbacks when the last observer is just removed. */
+ protected abstract fun onLastObserverRemoved()
+
+ override fun hasAnyObserver() = counter.get() > 0
+}
+
/** [KeyedObservable] with no-op implementations for all interfaces. */
open class NoOpKeyedObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 04d4bfe0741d..3f1a499807dd 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -20,21 +20,10 @@ import android.content.ContentResolver
import android.database.ContentObserver
import android.net.Uri
import android.util.Log
-import java.util.concurrent.Executor
-import java.util.concurrent.atomic.AtomicInteger
/** Base class of the Settings provider data stores. */
abstract class SettingsStore(protected val contentResolver: ContentResolver) :
- KeyedDataObservable<String>(), KeyValueStore {
-
- /**
- * Counter of observers.
- *
- * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When
- * an observer is not removed (and its weak reference is garbage collected), the content
- * observer is not unregistered but this is not a big deal.
- */
- private val counter = AtomicInteger()
+ AbstractKeyedDataObservable<String>(), KeyValueStore {
private val contentObserver =
object : ContentObserver(HandlerExecutor.main) {
@@ -48,84 +37,19 @@ abstract class SettingsStore(protected val contentResolver: ContentResolver) :
}
}
- override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
- if (super.addObserver(observer, executor)) {
- onObserverAdded()
- true
- } else {
- false
- }
-
- override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
- if (super.addObserver(key, observer, executor)) {
- onObserverAdded()
- true
- } else {
- false
- }
+ /** The URI to watch for any key change. */
+ protected abstract val uri: Uri
- private fun onObserverAdded() {
- if (counter.getAndIncrement() != 0) return
+ override fun onFirstObserverAdded() {
Log.i(tag, "registerContentObserver")
contentResolver.registerContentObserver(uri, true, contentObserver)
}
- /** The URI to watch for any key change. */
- protected abstract val uri: Uri
-
- override fun removeObserver(observer: KeyedObserver<String?>) =
- if (super.removeObserver(observer)) {
- onObserverRemoved()
- true
- } else {
- false
- }
-
- override fun removeObserver(key: String, observer: KeyedObserver<String>) =
- if (super.removeObserver(key, observer)) {
- onObserverRemoved()
- true
- } else {
- false
- }
-
- private fun onObserverRemoved() {
- if (counter.decrementAndGet() != 0) return
+ override fun onLastObserverRemoved() {
Log.i(tag, "unregisterContentObserver")
contentResolver.unregisterContentObserver(contentObserver)
}
- /** Gets the boolean value of given key. */
- fun getBoolean(key: String): Boolean? = getValue(key, Boolean::class.javaObjectType)
-
- /** Sets boolean value for given key, null value means delete the key from data store. */
- fun setBoolean(key: String, value: Boolean?) =
- setValue(key, Boolean::class.javaObjectType, value)
-
- /** Gets the float value of given key. */
- fun getFloat(key: String): Float? = getValue(key, Float::class.javaObjectType)
-
- /** Sets float value for given key, null value means delete the key from data store. */
- fun setFloat(key: String, value: Float?) = setValue(key, Float::class.javaObjectType, value)
-
- /** Gets the int value of given key. */
- fun getInt(key: String): Int? = getValue(key, Int::class.javaObjectType)
-
- /** Sets int value for given key, null value means delete the key from data store. */
- fun setInt(key: String, value: Int?) = setValue(key, Int::class.javaObjectType, value)
-
- /** Gets the long value of given key. */
- fun getLong(key: String): Long? = getValue(key, Long::class.javaObjectType)
-
- /** Sets long value for given key, null value means delete the key from data store. */
- fun setLong(key: String, value: Long?) = setValue(key, Long::class.javaObjectType, value)
-
- /** Gets the string value of given key. */
- fun getString(key: String): String? = getValue(key, String::class.javaObjectType)
-
- /** Sets string value for given key, null value means delete the key from data store. */
- fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
-
/** Tag for logging. */
abstract val tag: String
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index a768b5edb395..606710e6f356 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -55,9 +55,9 @@ import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.PreferenceScreenFactory
import com.android.settingslib.preference.PreferenceScreenProvider
+import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import java.util.Locale
private const val TAG = "PreferenceGraphBuilder"
@@ -399,15 +399,11 @@ fun PreferenceMetadata.toProto(
value = preferenceValueProto {
when (metadata) {
is BooleanValue ->
- metadata
- .storage(context)
- .getValue(metadata.key, Boolean::class.javaObjectType)
- ?.let { booleanValue = it }
+ metadata.storage(context).getBoolean(metadata.key)?.let {
+ booleanValue = it
+ }
is RangeValue -> {
- metadata
- .storage(context)
- .getValue(metadata.key, Int::class.javaObjectType)
- ?.let { intValue = it }
+ metadata.storage(context).getInt(metadata.key)?.let { intValue = it }
}
else -> {}
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index 7cfce0d85cd4..56b169370e47 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -146,7 +146,7 @@ class PreferenceSetterApiHandler(
val booleanValue = value.booleanValue
val resultCode = metadata.checkWritePermit(booleanValue)
if (resultCode != PreferenceSetterResult.OK) return resultCode
- storage.setValue(key, Boolean::class.javaObjectType, booleanValue)
+ storage.setBoolean(key, booleanValue)
return PreferenceSetterResult.OK
} else if (value.hasIntValue()) {
val intValue = value.intValue
@@ -155,7 +155,7 @@ class PreferenceSetterApiHandler(
if (metadata is RangeValue && !metadata.isValidValue(application, intValue)) {
return PreferenceSetterResult.INVALID_REQUEST
}
- storage.setValue(key, Int::class.javaObjectType, intValue)
+ storage.setInt(key, intValue)
return PreferenceSetterResult.OK
}
} catch (e: Exception) {
diff --git a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
index 2edc001ccc3f..43cf6aa09109 100644
--- a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
+++ b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
@@ -26,7 +26,6 @@
<ImageView
android:id="@android:id/icon"
- android:src="@drawable/settingslib_arrow_drop_down"
style="@style/SettingsLibEntityHeaderIcon"/>
<TextView
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 106802e9d1d1..73728bcd1ff7 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -32,6 +32,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.settingslib.widget.mainswitch.R;
@@ -42,7 +43,7 @@ import java.util.List;
/**
* MainSwitchBar is a View with a customized Switch.
* This component is used as the main switch of the page
- * to enable or disable the prefereces on the page.
+ * to enable or disable the preferences on the page.
*/
public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListener {
@@ -58,6 +59,8 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
protected CompoundButton mSwitch;
private final View mFrameView;
+ private @Nullable PreChangeListener mPreChangeListener;
+
public MainSwitchBar(Context context) {
this(context, null);
}
@@ -138,10 +141,20 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
@Override
public boolean performClick() {
- mSwitch.performClick();
+ if (callPreChangeListener()) {
+ mSwitch.performClick();
+ }
return super.performClick();
}
+ protected boolean callPreChangeListener() {
+ return mPreChangeListener == null || mPreChangeListener.preChange(!mSwitch.isChecked());
+ }
+
+ public void setPreChangeListener(@Nullable PreChangeListener preChangeListener) {
+ mPreChangeListener = preChangeListener;
+ }
+
/**
* Update the switch status
*/
@@ -271,7 +284,7 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
}
}
- static class SavedState extends BaseSavedState {
+ public static class SavedState extends BaseSavedState {
boolean mChecked;
boolean mVisible;
@@ -341,4 +354,16 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
requestLayout();
}
+
+ /**
+ * Listener callback before switch is toggled.
+ */
+ public interface PreChangeListener {
+ /**
+ * Returns if the new value can be set.
+ *
+ * When false is return, the switch toggle is not triggered at all.
+ */
+ boolean preChange(boolean isCheck);
+ }
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
index 6e86fa7312cf..c1edbdc20361 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -107,20 +107,11 @@ interface PreferenceMetadata {
*
* UI framework normally does not allow user to interact with the preference widget when it is
* disabled.
- *
- * [dependencyOfEnabledState] is provided to support dependency, the [shouldDisableDependents]
- * value of dependent preference is used to decide enabled state.
*/
- fun isEnabled(context: Context): Boolean {
- val dependency = dependencyOfEnabledState(context) ?: return true
- return !dependency.shouldDisableDependents(context)
- }
-
- /** Returns the key of depended preference to decide the enabled state. */
- fun dependencyOfEnabledState(context: Context): PreferenceMetadata? = null
+ fun isEnabled(context: Context): Boolean = true
- /** Returns whether this preference's dependents should be disabled. */
- fun shouldDisableDependents(context: Context): Boolean = !isEnabled(context)
+ /** Returns the keys of depended preferences. */
+ fun dependencies(context: Context): Array<String> = arrayOf()
/** Returns if the preference is persistent in datastore. */
fun isPersistent(context: Context): Boolean = this is PersistentPreference<*>
@@ -174,13 +165,11 @@ interface PreferenceMetadata {
}
/** Metadata of preference group. */
-@AnyThread
-interface PreferenceGroup : PreferenceMetadata
+@AnyThread interface PreferenceGroup : PreferenceMetadata
/** Metadata of preference category. */
@AnyThread
-open class PreferenceCategory(override val key: String, override val title: Int) :
- PreferenceGroup
+open class PreferenceCategory(override val key: String, override val title: Int) : PreferenceGroup
/** Metadata of preference screen. */
@AnyThread
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index 6704ecc93891..3dd15946d415 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -21,6 +21,7 @@ import android.content.ContextWrapper
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.settingslib.datastore.KeyValueStore
import kotlinx.coroutines.CoroutineScope
/**
@@ -157,6 +158,9 @@ abstract class PreferenceLifecycleContext(context: Context) : ContextWrapper(con
*/
abstract fun <T : Any> requirePreference(key: String): T
+ /** Returns the [KeyValueStore] attached to the preference of given key *on the same screen*. */
+ abstract fun getKeyValueStore(key: String): KeyValueStore?
+
/** Notifies that preference state of given key is changed and updates preference widget UI. */
abstract fun notifyPreferenceChange(key: String)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
index b64f5dc49b4b..87bd261bf4bd 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
@@ -16,19 +16,10 @@
package com.android.settingslib.metadata
-import android.content.Context
import androidx.annotation.StringRes
-/**
- * Common base class for preferences that have two selectable states, save a boolean value, and may
- * have dependent preferences that are enabled/disabled based on the current state.
- */
-interface TwoStatePreference : PreferenceMetadata, PersistentPreference<Boolean>, BooleanValue {
-
- override fun shouldDisableDependents(context: Context) =
- storage(context).getValue(key, Boolean::class.javaObjectType) != true ||
- super.shouldDisableDependents(context)
-}
+/** Common base class for preferences that have two selectable states and save a boolean value. */
+interface TwoStatePreference : PreferenceMetadata, PersistentPreference<Boolean>, BooleanValue
/** A preference that provides a two-state toggleable option. */
open class SwitchPreference
@@ -42,7 +33,4 @@ constructor(
/** A preference that provides a two-state toggleable option that can be used as a main switch. */
open class MainSwitchPreference
@JvmOverloads
-constructor(
- override val key: String,
- @StringRes override val title: Int = 0,
-) : TwoStatePreference \ No newline at end of file
+constructor(override val key: String, @StringRes override val title: Int = 0) : TwoStatePreference
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
index 7601b9a31041..f0f854aac79b 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
@@ -23,38 +23,31 @@ import com.android.settingslib.datastore.KeyValueStore
class PreferenceDataStoreAdapter(val keyValueStore: KeyValueStore) : PreferenceDataStore() {
override fun getBoolean(key: String, defValue: Boolean): Boolean =
- keyValueStore.getValue(key, Boolean::class.javaObjectType) ?: defValue
+ keyValueStore.getBoolean(key) ?: defValue
override fun getFloat(key: String, defValue: Float): Float =
- keyValueStore.getValue(key, Float::class.javaObjectType) ?: defValue
+ keyValueStore.getFloat(key) ?: defValue
- override fun getInt(key: String, defValue: Int): Int =
- keyValueStore.getValue(key, Int::class.javaObjectType) ?: defValue
+ override fun getInt(key: String, defValue: Int): Int = keyValueStore.getInt(key) ?: defValue
- override fun getLong(key: String, defValue: Long): Long =
- keyValueStore.getValue(key, Long::class.javaObjectType) ?: defValue
+ override fun getLong(key: String, defValue: Long): Long = keyValueStore.getLong(key) ?: defValue
override fun getString(key: String, defValue: String?): String? =
- keyValueStore.getValue(key, String::class.javaObjectType) ?: defValue
+ keyValueStore.getString(key) ?: defValue
@Suppress("UNCHECKED_CAST")
override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? =
(keyValueStore.getValue(key, Set::class.javaObjectType) as Set<String>?) ?: defValues
- override fun putBoolean(key: String, value: Boolean) =
- keyValueStore.setValue(key, Boolean::class.javaObjectType, value)
+ override fun putBoolean(key: String, value: Boolean) = keyValueStore.setBoolean(key, value)
- override fun putFloat(key: String, value: Float) =
- keyValueStore.setValue(key, Float::class.javaObjectType, value)
+ override fun putFloat(key: String, value: Float) = keyValueStore.setFloat(key, value)
- override fun putInt(key: String, value: Int) =
- keyValueStore.setValue(key, Int::class.javaObjectType, value)
+ override fun putInt(key: String, value: Int) = keyValueStore.setInt(key, value)
- override fun putLong(key: String, value: Long) =
- keyValueStore.setValue(key, Long::class.javaObjectType, value)
+ override fun putLong(key: String, value: Long) = keyValueStore.setLong(key, value)
- override fun putString(key: String, value: String?) =
- keyValueStore.setValue(key, String::class.javaObjectType, value)
+ override fun putString(key: String, value: String?) = keyValueStore.setString(key, value)
override fun putStringSet(key: String, values: Set<String>?) =
keyValueStore.setValue(key, Set::class.javaObjectType, values)
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 6fc9357e9332..a9e20f284a61 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -67,6 +67,11 @@ class PreferenceScreenBindingHelper(
override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!!
+ override fun getKeyValueStore(key: String) =
+ (findPreference<Preference>(key)?.preferenceDataStore
+ as? PreferenceDataStoreAdapter)
+ ?.keyValueStore
+
override fun notifyPreferenceChange(key: String) =
notifyChange(key, CHANGE_REASON_STATE)
@@ -92,14 +97,14 @@ class PreferenceScreenBindingHelper(
val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>()
val dependenciesBuilder = ImmutableMultimap.builder<String, String>()
val lifecycleAwarePreferences = mutableListOf<PreferenceLifecycleProvider>()
- fun PreferenceMetadata.addDependency(dependency: PreferenceMetadata) {
- dependenciesBuilder.put(key, dependency.key)
- }
fun PreferenceHierarchyNode.addNode() {
metadata.let {
- preferencesBuilder.put(it.key, this)
- it.dependencyOfEnabledState(context)?.addDependency(it)
+ val key = it.key
+ preferencesBuilder.put(key, this)
+ for (dependency in it.dependencies(context)) {
+ dependenciesBuilder.put(dependency, key)
+ }
if (it is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(it)
}
}
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
index 4bd1d451ce8f..b41ec957f12d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Geaktiveer deur administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Gedeaktiveer deur administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Geaktiveer deur Gevorderde Beskerming"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gedeaktiveer deur Gevorderde Beskerming"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
index ef380fd343dd..8e9488453032 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"በአስተዳዳሪ ነቅቷል"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"በአስተዳዳሪ ተሰናክሏል"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"በላቀ ጥበቃ የነቃ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"በላቀ ጥበቃ የተሰናከለ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
index 57cf30f1c3c7..8b2ccdfbb0e8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"يفعِّل المشرف هذا الإعداد."</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"أوقف المشرف هذا الإعداد"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"تم التفعيل من خلال ميزة \"الحماية المتقدّمة\""</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"تم الإيقاف من خلال ميزة \"الحماية المتقدّمة\""</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
index 21fe73ae59b3..03e9e828534a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"প্ৰশাসকে সক্ষম কৰিছে"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"প্ৰশাসকে অক্ষম কৰিছে"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে সক্ষম কৰিছে"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে অক্ষম কৰিছে"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
index c7c4c388aca0..98447166a156 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Admin tərəfindən aktiv edildi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Admin tərəfindən deaktiv edildi"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Qabaqcıl Qoruma tərəfindən aktiv edilib"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Qabaqcıl Qoruma tərəfindən deaktiv edilib"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
index a7d139577967..c7b9be28fb1e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administrator je omogućio"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administrator je onemogućio"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je Napredna zaštita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je Napredna zaštita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
index 60b9fc6c3cb0..92ed11157dfc 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Уключана адміністратарам"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Адключана адміністратарам"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Уключана Палепшанай абаронай"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Адключана Палепшанай абаронай"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
index f98a8d43cff5..57b50c5c580c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Активирано от администратора"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Деактивирано от администратора"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Активирано от „Разширена защита“"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Деактивирано от „Разширена защита“"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
index 87e8bd2969ba..939ceb82ab40 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"অ্যাডমিন চালু করেছেন"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"অ্যাডমিন বন্ধ করেছেন"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"উন্নত সুরক্ষা চালু করেছে"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"উন্নত সুরক্ষা বন্ধ করেছে"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
index a56917f9d286..87cd3b8905c7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućeno je Naprednom zaštitom"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućeno je Naprednom zaštitom"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
index ac38bd1f439c..34099b6f3f08 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activat per l\'administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desactivat per l\'administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activat per la Protecció avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desactivat per la Protecció avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
index 1840ecd3609b..82cd56f39059 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Zapnuto administrátorem"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Vypnuto administrátorem"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivováno pokročilou ochranou"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivováno pokročilou ochranou"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
index 2e6dd0f04bb8..7f7ae8b3d5e0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktiveret af administratoren"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Deaktiveret af administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiveret af Avanceret beskyttelse"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktiveret af Avanceret beskyttelse"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
index 88c146cc9e35..efaa50efacf4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Vom Administrator aktiviert"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Vom Administrator deaktiviert"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Vom erweiterten Sicherheitsprogramm aktiviert"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Vom erweiterten Sicherheitsprogramm deaktiviert"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
index 941d4ba5ebc2..ddde3ece472a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ενεργοποιήθηκε από τον διαχειριστή"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Απενεργοποιήθηκε από τον διαχειριστή"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Απενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
index 91268ec2f896..6a07741d8e4c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
index 91268ec2f896..6a07741d8e4c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
index 91268ec2f896..6a07741d8e4c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
index 91268ec2f896..6a07741d8e4c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
index 9ea88df3ff62..8dc15f77f6e0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"El administrador habilitó la opción"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"El administrador inhabilitó la opción"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por la Protección avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por la Protección avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
index 35777a231fff..7c9864d7e81e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Habilitado por el administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Inhabilitado por el administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por Protección Avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por Protección Avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
index 806d4b88ff5c..5939b584a0f2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administraatori lubatud"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administraatori keelatud"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lubatud täiustatud kaitsega"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Keelatud täiustatud kaitsega"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
index 4fe9b23eeaba..27bef6e92073 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administratzaileak gaitu egin du"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administratzaileak desgaitu du"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Babes aurreratua programak gaitu du"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Babes aurreratua programak desgaitu du"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
index 774cb9f15e10..8fb2646821dc 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"توسط سرپرست فعال شده"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"توسط سرپرست غیرفعال شده"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"فعال‌شده با «محافظت پیشرفته»"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"غیرفعال‌شده با «محافظت پیشرفته»"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
index c15882b3f650..cd7cbd3d83ca 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Järjestelmänvalvojan sallima"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Järjestelmänvalvojan estämä"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lisäsuojaus on ottanut asetuksen käyttöön"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Lisäsuojaus on poistanut asetuksen käytöstä"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
index 188e9cbf72a7..8f0d572aa193 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activé par la protection avancée"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivé par la protection avancée"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
index 188e9cbf72a7..2215af24664c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activé par la Protection Avancée"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivé par la Protection Avancée"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
index de603d05801a..92f33bbf2533 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Opción activada polo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Opción desactivada polo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Opción activada por Protección avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Opción desactivada por Protección avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
index 3ea8c49a1c08..026bdb1127d3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"વ્યવસ્થાપકે ચાલુ કરેલ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ઍડમિને બંધ કરેલું"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"અદ્યતન સુરક્ષા દ્વારા ચાલુ કરવામાં આવી છે"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"અદ્યતન સુરક્ષા દ્વારા બંધ કરવામાં આવી છે"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
index 93ac326e7361..8fc8fd04ad93 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"एडमिन की ओर से चालू किया गया"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन ने यह सुविधा बंद की हुई है"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"\'ऐडवांस सुरक्षा\' सेटिंग ने चालू किया है"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"\'ऐडवांस सुरक्षा\' सेटिंग ने बंद किया है"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
index a56917f9d286..40605a3b83fb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je napredna zaštita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je napredna zaštita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
index 897729d12fa8..59135a42fd2a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"A rendszergazda bekapcsolta"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"A rendszergazda letiltotta"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Engedélyezte a Speciális védelem"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Letiltotta a Speciális védelem"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
index 7c8e3492516f..0221f9333249 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Միացված է ադմինիստրատորի կողմից"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Անջատվել է ադմինիստրատորի կողմից"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Միացվել է Լրացուցիչ պաշտպանության կողմից"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Անջատվել է Լրացուցիչ պաշտպանության կողմից"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
index 0b08fdbd9620..6beb4c9b2c26 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Diaktifkan oleh admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dinonaktifkan oleh admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Diaktifkan oleh Perlindungan Lanjutan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dinonaktifkan oleh Perlindungan Lanjutan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
index 6a041c77def5..feb325bf2210 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Gert virkt af kerfisstjóra"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Gert óvirkt af kerfisstjóra"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Virkjað af ítarlegri vernd"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gert óvirkt af ítarlegri vernd"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
index ee78ae8f11de..616392026b55 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Attivata dall\'amministratore"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Opzione disattivata dall\'amministratore"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Attivata dalla protezione avanzata"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disattivata dalla protezione avanzata"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
index c24331841a1b..c342041f6c6e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"מופעל על ידי מנהל המכשיר"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"האפשרות הושבתה על ידי האדמין"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ההעדפה הופעלה על ידי ההגנה המתקדמת"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ההעדפה הושבתה על ידי ההגנה המתקדמת"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
index e4aa5c9b1569..bd386f5858a9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"管理者によって有効にされています"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"管理者により無効にされています"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"高度な保護機能により有効になっています"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"高度な保護機能により無効になっています"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
index 86248f681efa..a6fde9022a21 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"ჩართულია ადმინისტრატორის მიერ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"გათიშულია ადმინისტრატორის მიერ"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ჩართულია დამატებითი დაცვის საშუალებით"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"გათიშულია დამატებითი დაცვის საშუალებით"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
index b945c07a5a56..ed0f95c200f6 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Әкімші қосқан"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Әкімші өшірген"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Күшейтілген қорғаныс параметрі қосып қойды."</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Күшейтілген қорғаныс параметрі өшіріп тастады."</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
index 9deadfd996a0..f2f5ab8e1dbf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"បើកដោយ​អ្នកគ្រប់គ្រង"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"បានបិទដោយអ្នកគ្រប់គ្រង"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"បានបើកដោយការ​ការពារ​កម្រិតខ្ពស់"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"បានបិទដោយការ​ការពារ​កម្រិតខ្ពស់"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
index 2af7fe2aa327..ebc41a52b4df 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"ನಿರ್ವಾಹಕರು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
index 62b781f9a87b..552662b4be95 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"관리자가 사용 설정함"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"관리자가 사용 중지함"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"고급 보호 기능으로 사용 설정됨"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"고급 보호 기능으로 사용 중지됨"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
index 8437e9e8b2c2..375ea19ff66e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Администратор иштетип койгон"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Администратор өчүрүп койгон"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Өркүндөтүлгөн коргоо тарабынан иштетилди"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Өркүндөтүлгөн коргоо тарабынан өчүрүлдү"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
index be21bf4bd03e..4b311c0ce38d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"ເປີດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ຖືກຜູ້ເບິ່ງແຍງລະບົບປິດໄວ້"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ໄດ້ເປີດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ໄດ້ປິດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
index b290a54e0d57..cbbe92374854 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Įgalino administratorius"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Išjungė administratorius"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Įgalino Papildoma apsauga"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Išjungė Papildoma apsauga"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
index 5f86f984e8be..a5189aa70ff8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Iespējoja administrators"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Atspējoja administrators"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Iespējota iestatījuma “Papildu aizsardzība” dēļ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Atspējota iestatījuma “Papildu aizsardzība” dēļ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
index e3f0e75f39cd..993b4aea8b13 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Овозможено од администраторот"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Оневозможено од администраторот"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Овозможено од „Напредна заштита“"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Оневозможено од „Напредна заштита“"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
index fe1a4ae0d1e2..9deeb6aa2cfd 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"അഡ്‌മിൻ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"അഡ്‌മിൻ പ്രവർത്തനരഹിതമാക്കി"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനരഹിതമാക്കി"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
index c3799c36616b..c9a91de26591 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Админ идэвхжүүлсэн"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Админ цуцалсан"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Дэвшилтэт хамгаалалтаар идэвхжүүлсэн"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Дэвшилтэт хамгаалалтаар идэвхгүй болгосон"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
index 4e4660b376cb..ede12424de04 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"अ‍ॅडमिनने सुरू केलेले"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"अ‍ॅडमिनने बंद केलेले"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"प्रगत संरक्षणाद्वारे सुरू केले आहे"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"प्रगत संरक्षणाद्वारे बंद केले आहे"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
index 4bfcf2e59214..e8f710a7c909 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Didayakan oleh pentadbir"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dilumpuhkan oleh pentadbir"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Didayakan oleh Perlindungan Lanjutan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dilumpuhkan oleh Perlindungan Lanjutan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
index ef9ab1a987f7..97be99f43291 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"စီမံခန့်ခွဲသူက ဖွင့်ထားသည်"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"စီမံခန့်ခွဲသူက ပိတ်ထားသည်"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"အဆင့်မြင့်ကာကွယ်ရေးက ဖွင့်ထားသည်"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"အဆင့်မြင့်ကာကွယ်ရေးက ပိတ်ထားသည်"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
index 423d7ceb7f5c..971d1ccd190a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktivert av administratoren"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Deaktivert av administratoren"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivert av Avansert beskyttelse"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivert av Avansert beskyttelse"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
index 8793a81853df..3d2a74e3b270 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"प्रशासकद्वारा सक्षम पारिएको"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"एडमिनले अफ गरेको"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"सुरक्षासम्बन्धी उन्नत सुविधाले अन गरेको छ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"सुरक्षासम्बन्धी उन्नत सुविधाले अफ गरेको छ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
index 60bb02178c24..9830363c7f65 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aangezet door beheerder"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Uitgezet door beheerder"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aangezet door Geavanceerde beveiliging"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Uitgezet door Geavanceerde beveiliging"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
index 92004fb20def..2dab1592c296 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"ଆଡମିନଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ଆଡମିନଙ୍କ ଦ୍ଵାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
index 144dd4ad276a..12f296a46b2d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤੀ ਗਈ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
index 13814e13be05..df7394766339 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Włączone przez administratora"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Wyłączone przez administratora"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Włączone przez Ochronę zaawansowaną"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Wyłączone przez Ochronę zaawansowaną"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
index 8e97ffc4c0d6..a705ba421910 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
index 8e97ffc4c0d6..b01118381478 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ativado pela Proteção avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desativado pela Proteção avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
index 8e97ffc4c0d6..a705ba421910 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
index 6120ef6e4290..3eb347abbc1b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activat de administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dezactivat de administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activată de Protecția avansată"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dezactivată de Protecția avansată"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
index f06aa0641fd2..a004a1fe60da 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Включено администратором"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Отключено администратором"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Включено Дополнительной защитой"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Отключено Дополнительной защитой"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
index c9a9e23f903e..addc8b3ae15c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"පරිපාලක විසින් සබල කර ඇත"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ඔබගේ පරිපාලක විසින් අබල කර ඇත"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"උසස් ආරක්ෂණය මගින් සබල කර ඇත"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"උසස් ආරක්ෂණය මගින් අබල කර ඇත"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
index f6c1b6666983..0277696560b9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Povolené správcom"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Zakázané správcom"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivované rozšírenou ochranou"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivované rozšírenou ochranou"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
index 029cdefec092..eb886bcf1232 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogočil skrbnik"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogočil skrbnik"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogočila dodatna zaščita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogočila dodatna zaščita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
index 341d6bb29cc4..2de5ffe47c1c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktivizuar nga administratori"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Çaktivizuar nga administratori"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivizuar nga \"Mbrojtja e përparuar\""</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Çaktivizuar nga \"Mbrojtja e përparuar\""</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
index a783a02f1733..94f52a0b95a2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Администратор је омогућио"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Администратор је онемогућио"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Омогућила је Напредна заштита"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Онемогућила је Напредна заштита"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
index c930d4745ca4..b41c4d8a8275 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktiverad av administratör"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Inaktiverad av administratören"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiverades av Avancerat skydd"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inaktiverades av Avancerat skydd"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
index d61c8fbc962f..4d2e0d994972 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Imewashwa na msimamizi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Imezimwa na msimamizi"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Imewashwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Imezimwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
index b39f76ff55df..55b300657eaf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"நிர்வாகி இயக்கியுள்ளார்"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"நிர்வாகி முடக்கியுள்ளார்"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"மேம்பட்ட பாதுகாப்பு அமைப்பால் இயக்கப்பட்டது"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"மேம்பட்ட பாதுகாப்பு அமைப்பால் முடக்கப்பட்டது"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
index 4b8cd74e77c2..fc6d00b22d34 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"అడ్మిన్ ఎనేబుల్ చేశారు"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"అడ్మిన్ డిజేబుల్ చేశారు"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"అడ్వాన్స్‌డ్ ప్రొటెక్షన్ ద్వారా ఎనేబుల్ చేయబడింది"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"అడ్వాన్స్‌డ్ ప్రొటెక్షన్ ద్వారా డిజేబుల్ చేయబడింది"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
index 414d3c1fb2b5..51da8dec6443 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"เปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"ปิดใช้โดยผู้ดูแลระบบ"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"เปิดใช้โดยการปกป้องขั้นสูง"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ปิดใช้โดยการปกป้องขั้นสูง"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
index 34a057d3724b..7fbf0e7ace73 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Na-enable ng admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Na-disable ng admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Na-enable ng Advanced na Proteksyon"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Na-disable ng Advanced na Proteksyon"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
index f8d1f557f2dd..a529ca557ba1 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Yönetici tarafından etkinleştirildi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Yönetici tarafından devre dışı bırakıldı"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Gelişmiş Koruma tarafından etkinleştirildi"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gelişmiş Koruma tarafından devre dışı bırakıldı"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
index 2fa4634440d3..fdf4160b1bb0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Увімкнено адміністратором"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Вимкнено адміністратором"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Увімкнено Додатковим захистом"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Вимкнено Додатковим захистом"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
index 940130951ab4..b40cb6827523 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"منتظم کی طرف سے فعال کردہ"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"منتظم کی طرف سے غیر فعال کردہ"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"اعلی تحفظ نے فعال کیا ہے"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"اعلی تحفظ نے غیر فعال کیا ہے"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
index a726e6cec473..9a27735407e2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administrator tomonidan yoqilgan"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administrator tomonidan faolsizlantirilgan"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kuchaytirilgan himoya tomonidan yoqilgan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kuchaytirilgan himoya tomonidan faolsizlantirilgan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
index 63bb27ab8d9c..3436762f8355 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Do quản trị viên bật"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Quản trị viên đã vô hiệu hóa chế độ này"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Được bật bởi chế độ Bảo vệ nâng cao"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Bị tắt bởi chế độ Bảo vệ nâng cao"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
index 5698cf3a81ec..5c9e302f274c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"已被管理员启用"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"已被管理员停用"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已被“高级保护”功能启用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已被“高级保护”功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
index f37ccd89364b..d4b883355cf3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"已由管理員啟用"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"已由管理員停用"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
index f37ccd89364b..d4b883355cf3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"已由管理員啟用"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"已由管理員停用"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
index 848992574c97..2a93d00d687f 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Kunikwe amandla umlawuli"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Kukhutshazwe umlawuli"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kunikwe Amandla Ukuvikela Okuthuthukile"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kukhutshazwe Ukuvikela Okuthuthukile"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 63fe1b509751..e173c5e996df 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -25,13 +25,15 @@ import android.widget.Spinner;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
+
import com.android.settingslib.widget.spinner.R;
/**
* This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for
* both view and drop down view of the Spinner.
*/
-public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
+public class SettingsSpinnerPreference extends Preference
+ implements OnPreferenceClickListener, GroupSectionDividerMixin {
private SettingsSpinnerAdapter mAdapter;
private AdapterView.OnItemSelectedListener mListener;
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant12.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant12.xml
new file mode 100644
index 000000000000..f125425d1ec9
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant12.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="12"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant17.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant17.xml
new file mode 100644
index 000000000000..36a781954e42
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant17.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="17"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant22.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant22.xml
new file mode 100644
index 000000000000..0ef31d014aa2
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant22.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="22"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant24.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant24.xml
new file mode 100644
index 000000000000..6797f82e4250
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant24.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="24"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant4.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant4.xml
new file mode 100644
index 000000000000..ff7df5543a40
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant4.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="4"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant6.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant6.xml
new file mode 100644
index 000000000000..8da5dafea567
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant6.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="6"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant87.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant87.xml
new file mode 100644
index 000000000000..227baeedd99e
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant87.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="87"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant92.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant92.xml
new file mode 100644
index 000000000000..f4564381eb33
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant92.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="92"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant94.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant94.xml
new file mode 100644
index 000000000000..bb4e03d64307
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant94.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="94"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant96.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant96.xml
new file mode 100644
index 000000000000..949b1961099f
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant96.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="96"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant98.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant98.xml
new file mode 100644
index 000000000000..7e5ee241ffbd
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_neutral_variant98.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_600" android:lStar="98"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_check.xml
index 309dbdf1ea96..309dbdf1ea96 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_check.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_close.xml
index e6df8a416922..e6df8a416922 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_close.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_switch_thumb_icon.xml
index 342729d7ee5a..342729d7ee5a 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_switch_thumb_icon.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
index ea7baa42a2c7..2261e58a961e 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
@@ -40,7 +40,7 @@
android:longClickable="false"
android:maxLines="10"
android:ellipsize="end"
- android:textAppearance="@style/TextAppearance.TopIntroText"/>
+ android:textAppearance="@style/TextAppearance.SettingsLib.BodyLarge"/>
<com.android.settingslib.widget.LinkableTextView
android:id="@+id/settingslib_expressive_learn_more"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_preference_text_frame.xml
index c837ff43e46b..db357f8ae13f 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_preference_text_frame.xml
@@ -32,8 +32,6 @@
android:layout_gravity="start"
android:textAlignment="viewStart"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -47,7 +45,5 @@
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:maxLines="10"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"/>
+ android:maxLines="10"/>
</RelativeLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_preference_category_no_title.xml
index f69fcd270919..f69fcd270919 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_preference_category_no_title.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 46ec62e7a5ef..8873116be306 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -58,4 +58,39 @@
<color name="settingslib_colorSurface">@color/settingslib_surface_dark</color>
<color name="settingslib_list_divider_color">@android:color/system_neutral1_700</color>
+
+ <color name="settingslib_materialColorPrimary">@android:color/system_accent1_200</color>
+ <color name="settingslib_materialColorOnPrimary">@android:color/system_accent1_800</color>
+ <color name="settingslib_materialColorPrimaryContainer">@android:color/system_accent1_700</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_accent1_100</color>
+ <color name="settingslib_materialColorPrimaryInverse">@android:color/system_accent1_600</color>
+ <color name="settingslib_materialColorSecondary">@android:color/system_accent2_200</color>
+ <color name="settingslib_materialColorOnSecondary">@android:color/system_accent2_800</color>
+ <color name="settingslib_materialColorSecondaryContainer">@android:color/system_accent2_700</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_accent2_100</color>
+ <color name="settingslib_materialColorTertiary">@android:color/system_accent3_200</color>
+ <color name="settingslib_materialColorOnTertiary">@android:color/system_accent3_800</color>
+ <color name="settingslib_materialColorTertiaryContainer">@android:color/system_accent3_700</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_accent3_100</color>
+ <color name="settingslib_materialColorError">@color/settingslib_error_200</color>
+ <color name="settingslib_materialColorOnError">@color/settingslib_error_800</color>
+ <color name="settingslib_materialColorErrorContainer">@color/settingslib_error_700</color>
+ <color name="settingslib_materialColorOnErrorContainer">@color/settingslib_error_100</color>
+ <color name="settingslib_materialColorOutline">@android:color/system_neutral2_400</color>
+ <color name="settingslib_materialColorOutlineVariant">@android:color/system_neutral2_700</color>
+ <color name="settingslib_materialColorBackground">@color/settingslib_neutral_variant6</color>
+ <color name="settingslib_materialColorOnBackground">@android:color/system_neutral1_100</color>
+ <color name="settingslib_materialColorSurface">@color/settingslib_neutral_variant6</color>
+ <color name="settingslib_materialColorOnSurface">@android:color/system_neutral1_100</color>
+ <color name="settingslib_materialColorSurfaceVariant">@android:color/system_neutral2_700</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_neutral2_200</color>
+ <color name="settingslib_materialColorSurfaceInverse">@android:color/system_neutral1_100</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_neutral1_800</color>
+ <color name="settingslib_materialColorSurfaceBright">@color/settingslib_neutral_variant24</color>
+ <color name="settingslib_materialColorSurfaceDim">@color/settingslib_neutral_variant6</color>
+ <color name="settingslib_materialColorSurfaceContainer">@color/settingslib_neutral_variant12</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">@color/settingslib_neutral_variant4</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_neutral2_900</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">@color/settingslib_neutral_variant17</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">@color/settingslib_neutral_variant22</color>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml
index 8cfe54f44fe5..00a1f27c162a 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml
@@ -42,4 +42,39 @@
<color name="settingslib_text_color_primary_device_default">@android:color/system_on_surface_dark</color>
<!--Deprecated. After sdk 35 don't use it. using materialColorOnSurfaceVariant-->
<color name="settingslib_text_color_secondary_device_default">@android:color/system_on_surface_variant_dark</color>
+
+ <color name="settingslib_materialColorPrimary">@android:color/system_primary_dark</color>
+ <color name="settingslib_materialColorOnPrimary">@android:color/system_on_primary_dark</color>
+ <color name="settingslib_materialColorPrimaryContainer">@android:color/system_primary_container_dark</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_on_primary_container_dark</color>
+ <color name="settingslib_materialColorPrimaryInverse">@android:color/system_primary_light</color>
+ <color name="settingslib_materialColorSecondary">@android:color/system_secondary_dark</color>
+ <color name="settingslib_materialColorOnSecondary">@android:color/system_on_secondary_dark</color>
+ <color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_dark</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_on_secondary_container_dark</color>
+ <color name="settingslib_materialColorTertiary">@android:color/system_tertiary_dark</color>
+ <color name="settingslib_materialColorOnTertiary">@android:color/system_on_tertiary_dark</color>
+ <color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_dark</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_on_tertiary_container_dark</color>
+ <color name="settingslib_materialColorError">@android:color/system_error_dark</color>
+ <color name="settingslib_materialColorOnError">@android:color/system_on_error_dark</color>
+ <color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_dark</color>
+ <color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_dark</color>
+ <color name="settingslib_materialColorOutline">@android:color/system_outline_dark</color>
+ <color name="settingslib_materialColorOutlineVariant">@android:color/system_outline_variant_dark</color>
+ <color name="settingslib_materialColorBackground">@android:color/system_background_dark</color>
+ <color name="settingslib_materialColorOnBackground">@android:color/system_on_background_dark</color>
+ <color name="settingslib_materialColorSurface">@android:color/system_surface_dark</color>
+ <color name="settingslib_materialColorOnSurface">@android:color/system_on_surface_dark</color>
+ <color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_dark</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_on_surface_variant_dark</color>
+ <color name="settingslib_materialColorSurfaceInverse">@android:color/system_surface_light</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_on_surface_light</color>
+ <color name="settingslib_materialColorSurfaceBright">@android:color/system_surface_bright_dark</color>
+ <color name="settingslib_materialColorSurfaceDim">@android:color/system_surface_dim_dark</color>
+ <color name="settingslib_materialColorSurfaceContainer">@android:color/system_surface_container_dark</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_surface_container_low_dark</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">@android:color/system_surface_container_lowest_dark</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">@android:color/system_surface_container_high_dark</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">@android:color/system_surface_container_highest_dark</color>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
index 84a3ed68af01..e31e80176625 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
@@ -46,37 +46,4 @@
<color name="settingslib_colorSurfaceHeader">@color/settingslib_materialColorSurfaceVariant</color>
<color name="settingslib_text_color_preference_category_title">@color/settingslib_materialColorPrimary</color>
-
- <color name="settingslib_materialColorSurfaceContainerLowest">@android:color/system_surface_container_lowest_dark</color>
- <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_on_secondary_container_dark</color>
- <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_on_tertiary_container_dark</color>
- <color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_surface_container_low_dark</color>
- <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_on_primary_container_dark</color>
- <color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_dark</color>
- <color name="settingslib_materialColorInverseOnSurface">@android:color/system_on_surface_light</color>
- <color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_dark</color>
- <color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_dark</color>
- <color name="settingslib_materialColorInversePrimary">@android:color/system_primary_light</color>
- <color name="settingslib_materialColorInverseSurface">@android:color/system_surface_light</color>
- <color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_dark</color>
- <color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_dark</color>
- <color name="settingslib_materialColorPrimaryContainer">@android:color/system_primary_container_dark</color>
- <color name="settingslib_materialColorOnBackground">@android:color/system_on_background_dark</color>
- <color name="settingslib_materialColorOnSecondary">@android:color/system_on_secondary_dark</color>
- <color name="settingslib_materialColorOnTertiary">@android:color/system_on_tertiary_dark</color>
- <color name="settingslib_materialColorSurfaceDim">@android:color/system_surface_dim_dark</color>
- <color name="settingslib_materialColorSurfaceBright">@android:color/system_surface_bright_dark</color>
- <color name="settingslib_materialColorOnError">@android:color/system_on_error_dark</color>
- <color name="settingslib_materialColorSurface">@android:color/system_surface_dark</color>
- <color name="settingslib_materialColorSurfaceContainerHigh">@android:color/system_surface_container_high_dark</color>
- <color name="settingslib_materialColorSurfaceContainerHighest">@android:color/system_surface_container_highest_dark</color>
- <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_on_surface_variant_dark</color>
- <color name="settingslib_materialColorOutline">@android:color/system_outline_dark</color>
- <color name="settingslib_materialColorOutlineVariant">@android:color/system_outline_variant_dark</color>
- <color name="settingslib_materialColorOnPrimary">@android:color/system_on_primary_dark</color>
- <color name="settingslib_materialColorOnSurface">@android:color/system_on_surface_dark</color>
- <color name="settingslib_materialColorSurfaceContainer">@android:color/system_surface_container_dark</color>
- <color name="settingslib_materialColorPrimary">@android:color/system_primary_dark</color>
- <color name="settingslib_materialColorSecondary">@android:color/system_secondary_dark</color>
- <color name="settingslib_materialColorTertiary">@android:color/system_tertiary_dark</color>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night/colors.xml
new file mode 100644
index 000000000000..e57fe4f512fe
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-night/colors.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<resources>
+ <color name="settingslib_materialColorPrimary">#83D6C7</color>
+ <color name="settingslib_materialColorOnPrimary">#003730</color>
+ <color name="settingslib_materialColorPrimaryContainer">#005047</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">#A1F1E2</color>
+ <color name="settingslib_materialColorPrimaryInverse">#A1F1E2</color>
+ <color name="settingslib_materialColorSecondary">#B1CCC6</color>
+ <color name="settingslib_materialColorOnSecondary">#1C342F</color>
+ <color name="settingslib_materialColorSecondaryContainer">#334C47</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">#CCE8E2</color>
+ <color name="settingslib_materialColorTertiary">#ADCAE5</color>
+ <color name="settingslib_materialColorOnTertiary">#123349</color>
+ <color name="settingslib_materialColorTertiaryContainer">#2D4960</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">#CEE7FF</color>
+ <color name="settingslib_materialColorError">#F2B8B5</color>
+ <color name="settingslib_materialColorOnError">#601410</color>
+ <color name="settingslib_materialColorErrorContainer">#8C1D18</color>
+ <color name="settingslib_materialColorOnErrorContainer">#F9DEDC</color>
+ <color name="settingslib_materialColorOutline">#919191</color>
+ <color name="settingslib_materialColorOutlineVariant">#474747</color>
+ <color name="settingslib_materialColorBackground">#131313</color>
+ <color name="settingslib_materialColorOnBackground">#E5E2E1</color>
+ <color name="settingslib_materialColorSurface">#131313</color>
+ <color name="settingslib_materialColorOnSurface">#E5E2E1</color>
+ <color name="settingslib_materialColorSurfaceVariant">#474747</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">#C7C7C7</color>
+ <color name="settingslib_materialColorSurfaceInverse">#E5E2E1</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">#303030</color>
+ <color name="settingslib_materialColorSurfaceBright">#393939</color>
+ <color name="settingslib_materialColorSurfaceDim">#131313</color>
+ <color name="settingslib_materialColorSurfaceContainer">#1F1F1F</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">#1B1B1B</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">#0E0E0E</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">#2A2A2A</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">#343434</color>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index fef92b792bec..e000423784c6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -92,4 +92,51 @@
<color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
<color name="settingslib_list_divider_color">@android:color/system_neutral1_200</color>
+
+ <color name="settingslib_materialColorPrimary">@android:color/system_accent1_600</color>
+ <color name="settingslib_materialColorOnPrimary">@android:color/system_accent1_0</color>
+ <color name="settingslib_materialColorPrimaryContainer">@android:color/system_accent1_100</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_accent1_900</color>
+ <color name="settingslib_materialColorPrimaryInverse">@android:color/system_accent1_200</color>
+ <color name="settingslib_materialColorPrimaryFixed">@android:color/system_accent1_100</color>
+ <color name="settingslib_materialColorPrimaryFixedDim">@android:color/system_accent1_200</color>
+ <color name="settingslib_materialColorOnPrimaryFixed">@android:color/system_accent1_900</color>
+ <color name="settingslib_materialColorOnPrimaryFixedVariant">@android:color/system_accent1_700</color>
+ <color name="settingslib_materialColorSecondary">@android:color/system_accent2_600</color>
+ <color name="settingslib_materialColorOnSecondary">@android:color/system_accent2_0</color>
+ <color name="settingslib_materialColorSecondaryContainer">@android:color/system_accent2_100</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_accent2_900</color>
+ <color name="settingslib_materialColorSecondaryFixed">@android:color/system_accent2_100</color>
+ <color name="settingslib_materialColorSecondaryFixedDim">@android:color/system_accent2_200</color>
+ <color name="settingslib_materialColorOnSecondaryFixed">@android:color/system_accent2_900</color>
+ <color name="settingslib_materialColorOnSecondaryFixedVariant">@android:color/system_accent2_700</color>
+ <color name="settingslib_materialColorTertiary">@android:color/system_accent3_600</color>
+ <color name="settingslib_materialColorOnTertiary">@android:color/system_accent3_0</color>
+ <color name="settingslib_materialColorTertiaryContainer">@android:color/system_accent3_100</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_accent3_900</color>
+ <color name="settingslib_materialColorTertiaryFixed">@android:color/system_accent3_100</color>
+ <color name="settingslib_materialColorTertiaryFixedDim">@android:color/system_accent3_200</color>
+ <color name="settingslib_materialColorOnTertiaryFixed">@android:color/system_accent3_900</color>
+ <color name="settingslib_materialColorOnTertiaryFixedVariant">@android:color/system_accent3_700</color>
+ <color name="settingslib_materialColorError">@color/settingslib_error_600</color>
+ <color name="settingslib_materialColorOnError">@android:color/white</color>
+ <color name="settingslib_materialColorErrorContainer">@color/settingslib_error_100</color>
+ <color name="settingslib_materialColorOnErrorContainer">@color/settingslib_error_900</color>
+ <color name="settingslib_materialColorOutline">@android:color/system_neutral2_500</color>
+ <color name="settingslib_materialColorOutlineVariant">@android:color/system_neutral2_200</color>
+ <color name="settingslib_materialColorBackground">@android:color/white</color>
+ <color name="settingslib_materialColorOnBackground">@android:color/system_neutral1_900</color>
+ <color name="settingslib_materialColorSurface">@color/settingslib_neutral_variant98</color>
+ <color name="settingslib_materialColorOnSurface">@android:color/system_neutral1_900</color>
+ <color name="settingslib_materialColorSurfaceVariant">@android:color/system_neutral2_100</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_neutral2_700</color>
+ <color name="settingslib_materialColorSurfaceInverse">@android:color/system_neutral1_800</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_neutral1_50</color>
+ <color name="settingslib_materialColorSurfaceBright">@color/settingslib_neutral_variant98</color>
+ <color name="settingslib_materialColorSurfaceDim">@color/settingslib_neutral_variant87</color>
+ <color name="settingslib_materialColorSurfaceContainer">@color/settingslib_neutral_variant94</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">@color/settingslib_neutral_variant96</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">@android:color/system_neutral2_0</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">@color/settingslib_neutral_variant92</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">@android:color/system_neutral2_100</color>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
index 4860ad361744..8993d0fc71f7 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
@@ -17,6 +17,4 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<bool name="settingslib_config_icon_space_reserved">false</bool>
<bool name="settingslib_config_allow_divider">false</bool>
- <!-- Name of a font family to use for headlines in SettingsLib. -->
- <string name="settingslib_config_headlineFontFamily" translatable="false"></string>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml
new file mode 100644
index 000000000000..9d3d70b366aa
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources>
+ <style name="SettingsLibButtonStyle.Expressive.Filled"
+ parent="@style/Widget.Material3.Button">
+ <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:backgroundTint">@color/settingslib_materialColorPrimary</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
+ <item name="iconGravity">textStart</item>
+ <item name="iconTint">@color/settingslib_materialColorOnPrimary</item>
+ <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Filled.Large">
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Filled.Extra"
+ parent="@style/SettingsLibButtonStyle.Expressive.Filled.Large">
+ <item name="android:layout_width">match_parent</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Tonal"
+ parent="@style/Widget.Material3.Button.TonalButton">
+ <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:backgroundTint">@color/settingslib_materialColorSecondaryContainer</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item>
+ <item name="iconGravity">textStart</item>
+ <item name="iconTint">@color/settingslib_materialColorOnSecondaryContainer</item>
+ <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Tonal.Large">
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Tonal.Extra"
+ parent="@style/SettingsLibButtonStyle.Expressive.Tonal.Large">
+ <item name="android:layout_width">match_parent</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Outline"
+ parent="@style/Widget.Material3.Button.OutlinedButton.Icon">
+ <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item>
+ <item name="android:textColor">@color/settingslib_materialColorPrimary</item>
+ <item name="iconTint">@color/settingslib_materialColorPrimary</item>
+ <item name="iconGravity">textStart</item>
+ <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+ <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
+ <item name="strokeColor">@color/settingslib_materialColorOutlineVariant</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Outline.Large">
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item>
+ </style>
+
+ <style name="SettingsLibButtonStyle.Expressive.Outline.Extra"
+ parent="@style/SettingsLibButtonStyle.Expressive.Outline.Large">
+ <item name="android:layout_width">match_parent</item>
+ </style>
+
+ <style name="SettingslibTextButtonStyle.Expressive"
+ parent="@style/Widget.Material3.Button.TextButton.Icon">
+ <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.BodyLarge.Emphasized</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnSurface</item>
+ <item name="iconTint">@null</item>
+ <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
+ <item name="rippleColor">?android:attr/colorControlHighlight</item>
+ </style>
+
+ <style name="SettingsLibCardStyle" parent="">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginHorizontal">?android:attr/listPreferredItemPaddingStart</item>
+ <item name="android:layout_marginVertical">@dimen/settingslib_expressive_space_extrasmall4</item>
+ <item name="cardBackgroundColor">@color/settingslib_materialColorPrimary</item>
+ <item name="cardCornerRadius">@dimen/settingslib_expressive_radius_extralarge3</item>
+ <item name="cardElevation">0dp</item>
+ <item name="rippleColor">?android:attr/colorControlHighlight</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/styles_expressive.xml
new file mode 100644
index 000000000000..74bf55a8a625
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v33/styles_expressive.xml
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources>
+ <style name="TextAppearance.SettingsLib.DisplayLarge"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">57sp</item>
+ <item name="android:letterSpacing">-0.00438596</item>
+ <item name="android:lineHeight">64sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplayMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">45sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">52sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplaySmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">36sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">44sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.HeadlineLarge"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">32sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">40sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">28sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">36sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineSmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">24sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">32sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.TitleLarge"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">22sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">28sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight">24sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleSmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.LabelLarge"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.04166667</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelSmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">11sp</item>
+ <item name="android:letterSpacing">0.04545455</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.BodyLarge"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.03125</item>
+ <item name="android:lineHeight">24sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodyMedium"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01785714</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodySmall"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.03333333</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.DisplayLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">57sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">64sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplayMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">45sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">52sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplaySmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">36sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">44sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.HeadlineLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">32sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">40sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">28sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">36sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">24sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">32sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.TitleLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">22sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight">28sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight">24sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.LabelLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.04166667</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">11sp</item>
+ <item name="android:letterSpacing">0.04545455</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.BodyLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight">24sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodyMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01785714</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodySmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.03333333</item>
+ <item name="android:lineHeight">16sp</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml
index 185ac3e1fe73..60642e617a81 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml
@@ -43,4 +43,51 @@
<color name="settingslib_text_color_primary_device_default">@android:color/system_on_surface_light</color>
<!--Deprecated. After sdk 35 don't use it. using materialColorOnSurfaceVariant-->
<color name="settingslib_text_color_secondary_device_default">@android:color/system_on_surface_variant_light</color>
+
+ <color name="settingslib_materialColorPrimary">@android:color/system_primary_light</color>
+ <color name="settingslib_materialColorOnPrimary">@android:color/system_on_primary_light</color>
+ <color name="settingslib_materialColorPrimaryContainer">@android:color/system_primary_container_light</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_on_primary_container_light</color>
+ <color name="settingslib_materialColorPrimaryInverse">@android:color/system_primary_dark</color>
+ <color name="settingslib_materialColorPrimaryFixed">@android:color/system_primary_fixed</color>
+ <color name="settingslib_materialColorPrimaryFixedDim">@android:color/system_primary_fixed_dim</color>
+ <color name="settingslib_materialColorOnPrimaryFixed">@android:color/system_on_primary_fixed</color>
+ <color name="settingslib_materialColorOnPrimaryFixedVariant">@android:color/system_on_primary_fixed_variant</color>
+ <color name="settingslib_materialColorSecondary">@android:color/system_secondary_light</color>
+ <color name="settingslib_materialColorOnSecondary">@android:color/system_on_secondary_light</color>
+ <color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_light</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_on_secondary_container_light</color>
+ <color name="settingslib_materialColorSecondaryFixed">@android:color/system_secondary_fixed</color>
+ <color name="settingslib_materialColorSecondaryFixedDim">@android:color/system_secondary_fixed_dim</color>
+ <color name="settingslib_materialColorOnSecondaryFixed">@android:color/system_on_secondary_fixed</color>
+ <color name="settingslib_materialColorOnSecondaryFixedVariant">@android:color/system_on_secondary_fixed_variant</color>
+ <color name="settingslib_materialColorTertiary">@android:color/system_tertiary_light</color>
+ <color name="settingslib_materialColorOnTertiary">@android:color/system_on_tertiary_light</color>
+ <color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_light</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_on_tertiary_container_light</color>
+ <color name="settingslib_materialColorTertiaryFixed">@android:color/system_tertiary_fixed</color>
+ <color name="settingslib_materialColorTertiaryFixedDim">@android:color/system_tertiary_fixed_dim</color>
+ <color name="settingslib_materialColorOnTertiaryFixed">@android:color/system_on_tertiary_fixed</color>
+ <color name="settingslib_materialColorOnTertiaryFixedVariant">@android:color/system_on_tertiary_fixed_variant</color>
+ <color name="settingslib_materialColorError">@android:color/system_error_light</color>
+ <color name="settingslib_materialColorOnError">@android:color/system_on_error_light</color>
+ <color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_light</color>
+ <color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_light</color>
+ <color name="settingslib_materialColorOutline">@android:color/system_outline_light</color>
+ <color name="settingslib_materialColorOutlineVariant">@android:color/system_outline_variant_light</color>
+ <color name="settingslib_materialColorBackground">@android:color/system_background_light</color>
+ <color name="settingslib_materialColorOnBackground">@android:color/system_on_background_light</color>
+ <color name="settingslib_materialColorSurface">@android:color/system_surface_light</color>
+ <color name="settingslib_materialColorOnSurface">@android:color/system_on_surface_light</color>
+ <color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_light</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_on_surface_variant_light</color>
+ <color name="settingslib_materialColorSurfaceInverse">@android:color/system_surface_dark</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_on_surface_dark</color>
+ <color name="settingslib_materialColorSurfaceBright">@android:color/system_surface_bright_light</color>
+ <color name="settingslib_materialColorSurfaceDim">@android:color/system_surface_dim_light</color>
+ <color name="settingslib_materialColorSurfaceContainer">@android:color/system_surface_container_light</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_surface_container_low_light</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">@android:color/system_surface_container_lowest_light</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">@android:color/system_surface_container_high_light</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">@android:color/system_surface_container_highest_light</color>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
index 90c19e1aa676..b1b37b12c572 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
@@ -54,49 +54,4 @@
<color name="settingslib_spinner_title_color">@color/settingslib_materialColorOnPrimaryContainer</color>
<!-- The text color of dropdown item title -->
<color name="settingslib_spinner_dropdown_color">@color/settingslib_materialColorOnPrimaryContainer</color>
-
- <color name="settingslib_materialColorOnSecondaryFixedVariant">@android:color/system_on_secondary_fixed_variant</color>
- <color name="settingslib_materialColorOnTertiaryFixedVariant">@android:color/system_on_tertiary_fixed_variant</color>
- <color name="settingslib_materialColorSurfaceContainerLowest">@android:color/system_surface_container_lowest_light</color>
- <color name="settingslib_materialColorOnPrimaryFixedVariant">@android:color/system_on_primary_fixed_variant</color>
- <color name="settingslib_materialColorOnSecondaryContainer">@android:color/system_on_secondary_container_light</color>
- <color name="settingslib_materialColorOnTertiaryContainer">@android:color/system_on_tertiary_container_light</color>
- <color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_surface_container_low_light</color>
- <color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_on_primary_container_light</color>
- <color name="settingslib_materialColorSecondaryFixedDim">@android:color/system_secondary_fixed_dim</color>
- <color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_light</color>
- <color name="settingslib_materialColorOnSecondaryFixed">@android:color/system_on_secondary_fixed</color>
- <color name="settingslib_materialColorInverseOnSurface">@android:color/system_on_surface_dark</color>
- <color name="settingslib_materialColorTertiaryFixedDim">@android:color/system_tertiary_fixed_dim</color>
- <color name="settingslib_materialColorOnTertiaryFixed">@android:color/system_on_tertiary_fixed</color>
- <color name="settingslib_materialColorPrimaryFixedDim">@android:color/system_primary_fixed_dim</color>
- <color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_light</color>
- <color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_light</color>
- <color name="settingslib_materialColorOnPrimaryFixed">@android:color/system_on_primary_fixed</color>
- <color name="settingslib_materialColorInversePrimary">@android:color/system_primary_dark</color>
- <color name="settingslib_materialColorSecondaryFixed">@android:color/system_secondary_fixed</color>
- <color name="settingslib_materialColorInverseSurface">@android:color/system_surface_dark</color>
- <color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_light</color>
- <color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_light</color>
- <color name="settingslib_materialColorTertiaryFixed">@android:color/system_tertiary_fixed</color>
- <color name="settingslib_materialColorPrimaryContainer">@android:color/system_primary_container_light</color>
- <color name="settingslib_materialColorOnBackground">@android:color/system_on_background_light</color>
- <color name="settingslib_materialColorPrimaryFixed">@android:color/system_primary_fixed</color>
- <color name="settingslib_materialColorOnSecondary">@android:color/system_on_secondary_light</color>
- <color name="settingslib_materialColorOnTertiary">@android:color/system_on_tertiary_light</color>
- <color name="settingslib_materialColorSurfaceDim">@android:color/system_surface_dim_light</color>
- <color name="settingslib_materialColorSurfaceBright">@android:color/system_surface_bright_light</color>
- <color name="settingslib_materialColorOnError">@android:color/system_on_error_light</color>
- <color name="settingslib_materialColorSurface">@android:color/system_surface_light</color>
- <color name="settingslib_materialColorSurfaceContainerHigh">@android:color/system_surface_container_high_light</color>
- <color name="settingslib_materialColorSurfaceContainerHighest">@android:color/system_surface_container_highest_light</color>
- <color name="settingslib_materialColorOnSurfaceVariant">@android:color/system_on_surface_variant_light</color>
- <color name="settingslib_materialColorOutline">@android:color/system_outline_light</color>
- <color name="settingslib_materialColorOutlineVariant">@android:color/system_outline_variant_light</color>
- <color name="settingslib_materialColorOnPrimary">@android:color/system_on_primary_light</color>
- <color name="settingslib_materialColorOnSurface">@android:color/system_on_surface_light</color>
- <color name="settingslib_materialColorSurfaceContainer">@android:color/system_surface_container_light</color>
- <color name="settingslib_materialColorPrimary">@android:color/system_primary_light</color>
- <color name="settingslib_materialColorSecondary">@android:color/system_secondary_light</color>
- <color name="settingslib_materialColorTertiary">@android:color/system_tertiary_light</color>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
index 05a1ceacdb65..1a085681864a 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
@@ -16,150 +16,6 @@
-->
<resources>
- <style name="SettingsLibTextAppearance" parent="@android:style/TextAppearance.DeviceDefault">
- <!--item name="android:fontFamily"></item-->
- <item name="android:hyphenationFrequency">normalFast</item>
- <item name="android:lineBreakWordStyle">phrase</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Primary">
- <!--item name="android:fontFamily"></item-->
- </style>
-
- <style name="SettingsLibTextAppearance.Primary.Display">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Primary.Display.Large">
- <item name="android:textSize">57sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Display.Medium">
- <item name="android:textSize">45sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Display.Small">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Primary.Headline">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Primary.Headline.Large">
- <item name="android:textSize">32sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Headline.Medium">
- <item name="android:textSize">28sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Headline.Small">
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Primary.Title">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Primary.Title.Large">
- <item name="android:textSize">22sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Title.Medium">
- <item name="android:textSize">16sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Title.Small">
- <item name="android:textSize">14sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Primary.Label">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Primary.Label.Large">
- <item name="android:textSize">14sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Label.Medium">
- <item name="android:textSize">12sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Label.Small">
- <item name="android:textSize">11sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Primary.Body">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Primary.Body.Large">
- <item name="android:textSize">16sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Body.Medium">
- <item name="android:textSize">14sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Primary.Body.Small">
- <item name="android:textSize">12sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized">
- <!--item name="android:fontFamily"></item-->
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized.Display">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Display.Large">
- <item name="android:textSize">57sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Display.Medium">
- <item name="android:textSize">45sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Display.Small">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized.Headline">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Headline.Large">
- <item name="android:textSize">32sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Headline.Medium">
- <item name="android:textSize">28sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Headline.Small">
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized.Title">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Title.Large">
- <item name="android:textSize">22sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Title.Medium">
- <item name="android:textSize">16sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Title.Small">
- <item name="android:textSize">14sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized.Label">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Label.Large">
- <item name="android:textSize">14sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Label.Medium">
- <item name="android:textSize">12sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Label.Small">
- <item name="android:textSize">11sp</item>
- </style>
-
- <style name="SettingsLibTextAppearance.Emphasized.Body">
- <!--item name="android:fontFamily"></item-->
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Body.Large">
- <item name="android:textSize">16sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Body.Medium">
- <item name="android:textSize">14sp</item>
- </style>
- <style name="SettingsLibTextAppearance.Emphasized.Body.Small">
- <item name="android:textSize">12sp</item>
- </style>
-
<style name="SettingslibSwitchStyle.Expressive" parent="">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
@@ -175,122 +31,6 @@
<item name="trackTint">@color/settingslib_expressive_color_main_switch_track</item>
</style>
- <style name="SettingsLibCardStyle" parent="">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_marginHorizontal">?android:attr/listPreferredItemPaddingStart</item>
- <item name="android:layout_marginVertical">@dimen/settingslib_expressive_space_extrasmall4</item>
- <item name="cardBackgroundColor">@color/settingslib_materialColorPrimary</item>
- <item name="cardCornerRadius">@dimen/settingslib_expressive_radius_extralarge3</item>
- <item name="cardElevation">0dp</item>
- <item name="rippleColor">?android:attr/colorControlHighlight</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Filled"
- parent="@style/Widget.Material3.Button">
- <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:gravity">center</item>
- <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:backgroundTint">@color/settingslib_materialColorPrimary</item>
- <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
- <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
- <item name="android:textSize">14sp</item>
- <item name="iconGravity">textStart</item>
- <item name="iconTint">@color/settingslib_materialColorOnPrimary</item>
- <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Filled.Large">
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Filled.Extra"
- parent="@style/SettingsLibButtonStyle.Expressive.Filled.Large">
- <item name="android:layout_width">match_parent</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Tonal"
- parent="@style/Widget.Material3.Button.TonalButton">
- <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:gravity">center</item>
- <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:backgroundTint">@color/settingslib_materialColorSecondaryContainer</item>
- <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
- <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item>
- <item name="android:textSize">14sp</item>
- <item name="iconGravity">textStart</item>
- <item name="iconTint">@color/settingslib_materialColorOnSecondaryContainer</item>
- <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Tonal.Large">
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Tonal.Extra"
- parent="@style/SettingsLibButtonStyle.Expressive.Tonal.Large">
- <item name="android:layout_width">match_parent</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Outline"
- parent="@style/Widget.Material3.Button.OutlinedButton.Icon">
- <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:gravity">center</item>
- <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
- <item name="android:textColor">@color/settingslib_materialColorPrimary</item>
- <item name="android:textSize">14sp</item>
- <item name="iconTint">@color/settingslib_materialColorPrimary</item>
- <item name="iconGravity">textStart</item>
- <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
- <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
- <item name="strokeColor">@color/settingslib_materialColorOutlineVariant</item>
-
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Outline.Large">
- <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
- <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="SettingsLibButtonStyle.Expressive.Outline.Extra"
- parent="@style/SettingsLibButtonStyle.Expressive.Outline.Large">
- <item name="android:layout_width">match_parent</item>
- </style>
-
- <style name="SettingslibTextButtonStyle.Expressive"
- parent="@style/Widget.Material3.Button.TextButton.Icon">
- <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/settingslib_materialColorOnSurface</item>
- <item name="iconTint">@null</item>
- <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
- <item name="rippleColor">?android:attr/colorControlHighlight</item>
- </style>
-
<style name="EntityHeader">
<item name="android:paddingTop">@dimen/settingslib_expressive_space_small4</item>
<item name="android:paddingBottom">@dimen/settingslib_expressive_space_small1</item>
@@ -327,12 +67,11 @@
<item name="android:gravity">center</item>
<item name="android:ellipsize">marquee</item>
<item name="android:textDirection">locale</item>
- <item name="android:textAppearance">@style/TextAppearance.EntityHeaderTitle</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
</style>
<style name="SettingslibTextAppearance.LinkableTextStyle.Expressive"
- parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
- <item name="android:textSize">14sp</item>
+ parent="@style/TextAppearance.SettingsLib.LabelLarge">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
@@ -346,4 +85,14 @@
<item name="cardElevation">0dp</item>
<item name="rippleColor">?android:attr/colorControlHighlight</item>
</style>
+
+ <style name="TextAppearance.SettingsLib.PreferenceTitle"
+ parent="@style/TextAppearance.SettingsLib.TitleMedium">
+ <item name="android:textColor">@color/settingslib_text_color_primary</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.PreferenceSummary"
+ parent="@style/TextAppearance.SettingsLib.BodyMedium">
+ <item name="android:textColor">@color/settingslib_text_color_secondary</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
index fea8739ab37d..14f214a96435 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
@@ -18,8 +18,8 @@
<resources>
<style name="Theme.SettingsBase.Expressive">
<!-- Set up Preference title text style -->
- <!--item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item-->
- <!--item name="android:textAppearanceListItemSecondary">@style/textAppearanceListItemSecondary</item-->
+ <item name="android:textAppearanceListItem">@style/TextAppearance.SettingsLib.PreferenceTitle</item>
+ <item name="android:textAppearanceListItemSecondary">@style/TextAppearance.SettingsLib.PreferenceSummary</item>
<!-- Set up list item padding -->
<item name="android:listPreferredItemPaddingStart">@dimen/settingslib_expressive_space_small1</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/attrs_expressive.xml
index 857dd7953234..857dd7953234 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/attrs_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values/colors.xml b/packages/SettingsLib/SettingsTheme/res/values/colors.xml
new file mode 100644
index 000000000000..c5c613b4b329
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/colors.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<resources>
+ <color name="settingslib_error_0">#FFFFFF</color>
+ <color name="settingslib_error_10">#FFFBFF</color>
+ <color name="settingslib_error_50">#FFEDEA</color>
+ <color name="settingslib_error_100">#FFDAD6</color>
+ <color name="settingslib_error_200">#FFB4AB</color>
+ <color name="settingslib_error_300">#FF897D</color>
+ <color name="settingslib_error_400">#FF5449</color>
+ <color name="settingslib_error_500">#DE3730</color>
+ <color name="settingslib_error_600">#BA1A1A</color>
+ <color name="settingslib_error_700">#93000A</color>
+ <color name="settingslib_error_800">#690005</color>
+ <color name="settingslib_error_900">#410002</color>
+ <color name="settingslib_error_1000">#000000</color>
+
+ <color name="settingslib_materialColorPrimary">#006B5F</color>
+ <color name="settingslib_materialColorOnPrimary">#FFFFFF</color>
+ <color name="settingslib_materialColorPrimaryContainer">#C5EAE2</color>
+ <color name="settingslib_materialColorOnPrimaryContainer">#00201C</color>
+ <color name="settingslib_materialColorPrimaryInverse">#83D6C7</color>
+ <color name="settingslib_materialColorPrimaryFixed">#C5EAE2</color>
+ <color name="settingslib_materialColorPrimaryFixedDim">#82D5C6</color>
+ <color name="settingslib_materialColorOnPrimaryFixed">#00201C</color>
+ <color name="settingslib_materialColorOnPrimaryFixedVariant">#005047</color>
+ <color name="settingslib_materialColorSecondary">#4A635E</color>
+ <color name="settingslib_materialColorOnSecondary">#FFFFFF</color>
+ <color name="settingslib_materialColorSecondaryContainer">#CCE8E2</color>
+ <color name="settingslib_materialColorOnSecondaryContainer">#051F1B</color>
+ <color name="settingslib_materialColorSecondaryFixed">#CCE8E2</color>
+ <color name="settingslib_materialColorSecondaryFixedDim">#B1CCC6</color>
+ <color name="settingslib_materialColorOnSecondaryFixed">#051F1B</color>
+ <color name="settingslib_materialColorOnSecondaryFixedVariant">#334C47</color>
+ <color name="settingslib_materialColorTertiary">#456179</color>
+ <color name="settingslib_materialColorOnTertiary">#FFFFFF</color>
+ <color name="settingslib_materialColorTertiaryContainer">#CBE6FF</color>
+ <color name="settingslib_materialColorOnTertiaryContainer">#001E31</color>
+ <color name="settingslib_materialColorTertiaryFixed">#CBE5FF</color>
+ <color name="settingslib_materialColorTertiaryFixedDim">#ADCAE5</color>
+ <color name="settingslib_materialColorOnTertiaryFixed">#001E31</color>
+ <color name="settingslib_materialColorOnTertiaryFixedVariant">#2D4A60</color>
+ <color name="settingslib_materialColorError">#B3261E</color>
+ <color name="settingslib_materialColorOnError">#FFFFFF</color>
+ <color name="settingslib_materialColorErrorContainer">#F9DEDC</color>
+ <color name="settingslib_materialColorOnErrorContainer">#3A0A08</color>
+ <color name="settingslib_materialColorOutline">#777777</color>
+ <color name="settingslib_materialColorOutlineVariant">#C7C6C5</color>
+ <color name="settingslib_materialColorBackground">#F9FAF8</color>
+ <color name="settingslib_materialColorOnBackground">#1B1B1B</color>
+ <color name="settingslib_materialColorSurface">#F9FAF8</color>
+ <color name="settingslib_materialColorOnSurface">#1B1B1B</color>
+ <color name="settingslib_materialColorSurfaceVariant">#E3E3E3</color>
+ <color name="settingslib_materialColorOnSurfaceVariant">#474747</color>
+ <color name="settingslib_materialColorSurfaceInverse">#303030</color>
+ <color name="settingslib_materialColorOnSurfaceInverse">#F1F1F1</color>
+ <color name="settingslib_materialColorSurfaceBright">#F9FAF8</color>
+ <color name="settingslib_materialColorSurfaceDim">#DADADA</color>
+ <color name="settingslib_materialColorSurfaceContainer">#EEEEEE</color>
+ <color name="settingslib_materialColorSurfaceContainerLow">#F4F4F4</color>
+ <color name="settingslib_materialColorSurfaceContainerLowest">#FFFFFF</color>
+ <color name="settingslib_materialColorSurfaceContainerHigh">#E8E8E8</color>
+ <color name="settingslib_materialColorSurfaceContainerHighest">#E3E3E3</color>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/config.xml b/packages/SettingsLib/SettingsTheme/res/values/config.xml
index e73dcc0cc559..53da49180219 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/config.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/config.xml
@@ -16,4 +16,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<bool name="settingslib_config_icon_space_reserved">true</bool>
+
+ <!-- Name of a font family to use for headlines in SettingsLib. -->
+ <string name="settingslib_config_headlineFontFamily" translatable="false"></string>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens_expressive.xml
index 0542c510fa63..0542c510fa63 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/dimens_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
new file mode 100644
index 000000000000..f73e100906c8
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="TextAppearance.SettingsLib.DisplayLarge"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">57sp</item>
+ <item name="android:letterSpacing">-0.00438596</item>
+ <item name="android:lineHeight" tools:targetApi="28">64sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplayMedium"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">45sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">52sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplaySmall"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">36sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">44sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.HeadlineLarge"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">32sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">40sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineMedium"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">28sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">36sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineSmall"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">32sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.TitleLarge"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+ <item name="android:textSize">22sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">28sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight" tools:targetApi="28">24sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleSmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.LabelLarge"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelMedium"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.04166667</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelSmall"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">11sp</item>
+ <item name="android:letterSpacing">0.04545455</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.BodyLarge"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.03125</item>
+ <item name="android:lineHeight" tools:targetApi="28">24sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodyMedium"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01785714</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodySmall"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.03333333</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.DisplayLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">57sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">64sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplayMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">45sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">52sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.DisplaySmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">36sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">44sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.HeadlineLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">32sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">40sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">28sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">36sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.HeadlineSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">24sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">32sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.TitleLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">22sp</item>
+ <item name="android:letterSpacing">0</item>
+ <item name="android:lineHeight" tools:targetApi="28">28sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight" tools:targetApi="28">24sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.TitleSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.LabelLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.00714286</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.04166667</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.LabelSmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">11sp</item>
+ <item name="android:letterSpacing">0.04545455</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="TextAppearance.SettingsLib.BodyLarge.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:letterSpacing">0.009375</item>
+ <item name="android:lineHeight" tools:targetApi="28">24sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodyMedium.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01785714</item>
+ <item name="android:lineHeight" tools:targetApi="28">20sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+ <style name="TextAppearance.SettingsLib.BodySmall.Emphasized"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">12sp</item>
+ <item name="android:letterSpacing">0.03333333</item>
+ <item name="android:lineHeight" tools:targetApi="28">16sp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
index 265c065e924e..bfaeb42d5a31 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -16,6 +16,9 @@
package com.android.settingslib.widget
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.CallSuper
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.RecyclerView
@@ -23,9 +26,18 @@ import androidx.recyclerview.widget.RecyclerView
/** Base class for Settings to use PreferenceFragmentCompat */
abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
+ @CallSuper
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+ // Don't allow any divider in between the preferences in expressive design.
+ setDivider(null)
+ }
+ }
+
override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
return SettingsPreferenceGroupAdapter(preferenceScreen)
return super.onCreateAdapter(preferenceScreen)
}
-} \ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 02e190417853..cf695d0543c7 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidBasePlugin
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.kotlin.android) apply false
}
@@ -51,23 +50,4 @@ subprojects {
}
}
}
-
- afterEvaluate {
- plugins.withType<AndroidBasePlugin> {
- the(CommonExtension::class).apply {
- if (buildFeatures.compose == true) {
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
- }
- }
- }
- }
- }
-
- tasks.withType<KotlinCompile> {
- kotlinOptions {
- jvmTarget = libs.versions.jvm.get()
- freeCompilerArgs = listOf("-Xjvm-default=all")
- }
- }
}
diff --git a/packages/SettingsLib/Spa/gallery/build.gradle.kts b/packages/SettingsLib/Spa/gallery/build.gradle.kts
index a1151a5e827e..19aa710babd3 100644
--- a/packages/SettingsLib/Spa/gallery/build.gradle.kts
+++ b/packages/SettingsLib/Spa/gallery/build.gradle.kts
@@ -16,6 +16,7 @@
plugins {
alias(libs.plugins.android.application)
+ alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
index f7649b91f558..17f996567ce7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
@@ -20,11 +20,13 @@ import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.IntState
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -33,8 +35,11 @@ import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.widget.preference.ListPreferenceModel
+import com.android.settingslib.spa.widget.preference.ListPreferenceOption
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.RadioPreferences
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.SettingsIcon
@@ -103,6 +108,22 @@ object PreferencePageProvider : SettingsPageProvider {
override val summary = { ticks.toString() }
})
}
+ val selectedId = rememberSaveable { mutableIntStateOf(0) }
+ RadioPreferences(
+ object : ListPreferenceModel {
+ override val title: String = "RadioPreferences"
+ override val options: List<ListPreferenceOption> =
+ listOf(
+ ListPreferenceOption(id = 0, text = "option1"),
+ ListPreferenceOption(id = 1, text = "option2"),
+ ListPreferenceOption(id = 2, text = "option3"),
+ )
+ override val selectedId: IntState = selectedId
+ override val onIdSelected: (Int) -> Unit = {
+ selectedId.intValue = it
+ }
+ }
+ )
}
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 74811d3ae7a6..04ef96a89843 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,12 +15,11 @@
#
[versions]
-agp = "8.7.2"
-compose-compiler = "1.5.11"
+agp = "8.7.3"
dexmaker-mockito = "2.28.3"
jvm = "17"
-kotlin = "1.9.23"
-truth = "1.1.5"
+kotlin = "2.0.21"
+truth = "1.4.4"
[libraries]
dexmaker-mockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker-mockito" }
@@ -29,4 +28,5 @@ truth = { module = "com.google.truth:truth", version.ref = "truth" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 1f32ad6622a2..a0bbb0ca9ae6 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -16,6 +16,7 @@
plugins {
alias(libs.plugins.android.library)
+ alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
jacoco
}
@@ -41,9 +42,6 @@ android {
manifest.srcFile("../tests/AndroidManifest.xml")
}
}
- buildFeatures {
- compose = true
- }
buildTypes {
getByName("debug") {
enableAndroidTestCoverage = true
@@ -63,7 +61,7 @@ dependencies {
api("androidx.lifecycle:lifecycle-runtime-compose")
api("androidx.navigation:navigation-compose:2.9.0-alpha03")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
- api("com.google.android.material:material:1.12.0")
+ 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")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt
index 8300ce855988..ec94df057b77 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt
@@ -16,28 +16,34 @@
package com.android.settingslib.spa.widget.preference
-import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.IntState
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.compose.thenIf
import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.widget.ui.CategoryTitle
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
+import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.SettingsListItem
@Composable
fun RadioPreferences(model: ListPreferenceModel) {
- CategoryTitle(title = model.title)
- Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
- Column(modifier = Modifier.selectableGroup()) {
+ Category(modifier = Modifier.selectableGroup(), title = model.title) {
for (option in model.options) {
Radio2(option, model.selectedId.intValue, model.enabled()) {
model.onIdSelected(it)
@@ -54,20 +60,52 @@ fun Radio2(
onIdSelected: (id: Int) -> Unit,
) {
val selected = option.id == selectedId
+ val surfaceBright = MaterialTheme.colorScheme.surfaceBright
Row(
- modifier = Modifier
- .fillMaxWidth()
- .selectable(
- selected = selected,
- enabled = enabled,
- onClick = { onIdSelected(option.id) },
- role = Role.RadioButton,
- )
- .padding(SettingsDimension.dialogItemPadding),
+ modifier =
+ Modifier.fillMaxWidth()
+ .thenIf(isSpaExpressiveEnabled) {
+ Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
+ .background(surfaceBright)
+ }
+ .selectable(
+ selected = selected,
+ enabled = enabled,
+ onClick = { onIdSelected(option.id) },
+ role = Role.RadioButton,
+ )
+ .then(
+ if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.itemPadding)
+ else Modifier.padding(SettingsDimension.dialogItemPadding)
+ ),
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(selected = selected, onClick = null, enabled = enabled)
- Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
+ Spacer(
+ modifier =
+ Modifier.width(
+ if (isSpaExpressiveEnabled) SettingsDimension.paddingExtraSmall6
+ else SettingsDimension.itemDividerHeight
+ )
+ )
SettingsListItem(text = option.text, enabled = enabled)
}
-} \ No newline at end of file
+}
+
+@Preview
+@Composable
+private fun RadioPreferencePreview() {
+ RadioPreferences(
+ object : ListPreferenceModel {
+ override val title: String = "Title"
+ override val options: List<ListPreferenceOption> =
+ listOf(
+ ListPreferenceOption(id = 0, text = "option1"),
+ ListPreferenceOption(id = 1, text = "option2"),
+ ListPreferenceOption(id = 2, text = "option3"),
+ )
+ override val selectedId: IntState = remember { mutableIntStateOf(0) }
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ )
+}
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 96d2abb70391..62bc00a8b347 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
@@ -76,7 +76,7 @@ fun CategoryTitle(title: String) {
* visually separates groups of items.
*/
@Composable
-fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) {
+fun Category(title: String? = null, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
var displayTitle by remember { mutableStateOf(false) }
Column(
modifier =
@@ -90,7 +90,7 @@ fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit)
if (title != null && displayTitle) CategoryTitle(title = title)
Column(
modifier =
- Modifier.onGloballyPositioned { coordinates ->
+ modifier.onGloballyPositioned { coordinates ->
displayTitle = coordinates.size.height > 0
}
.then(
@@ -162,7 +162,7 @@ internal val LocalIsInCategory = compositionLocalOf { false }
@Composable
private fun CategoryPreview() {
SettingsTheme {
- Category("Appearance") {
+ Category(title = "Appearance") {
Preference(
object : PreferenceModel {
override val title = "Title"
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt
index 2f98b02b8dd5..d187017f5021 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt
@@ -43,7 +43,9 @@ class RadioPreferencesTest {
RadioPreferences(remember {
object : ListPreferenceModel {
override val title = TITLE
- override val options = emptyList<ListPreferenceOption>()
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A")
+ )
override val selectedId = mutableIntStateOf(0)
override val onIdSelected: (Int) -> Unit = {}
}
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts
index cce82354a51f..7dbd320c7d5a 100644
--- a/packages/SettingsLib/Spa/testutils/build.gradle.kts
+++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts
@@ -16,6 +16,7 @@
plugins {
alias(libs.plugins.android.library)
+ alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
}
@@ -30,9 +31,6 @@ android {
manifest.srcFile("AndroidManifest.xml")
}
}
- buildFeatures {
- compose = true
- }
}
dependencies {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index d89d3977cac3..5a524d944a24 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -133,7 +133,7 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
list.joinToString(separator = System.lineSeparator())
}
if (footer.isBlank()) return
- HorizontalDivider()
+ if (!isSpaExpressiveEnabled) HorizontalDivider()
Column(
modifier =
if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.footerItemPadding)
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 9a3e5b9e1e50..083b862e8a5c 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml
@@ -72,7 +72,7 @@
android:layout_height="wrap_content"
android:hyphenationFrequency="normalFast"
android:lineBreakWordStyle="phrase"
- android:textAppearance="@style/SettingsLibTextAppearance.Emphasized.Title.Large"/>
+ android:textAppearance="@style/TextAppearance.SettingsLib.TitleLarge.Emphasized"/>
<TextView
android:id="@android:id/summary"
@@ -81,7 +81,7 @@
android:hyphenationFrequency="normalFast"
android:lineBreakWordStyle="phrase"
android:maxLines="3"
- android:textAppearance="@style/SettingsLibTextAppearance.Primary.Body.Medium"/>
+ android:textAppearance="@style/TextAppearance.SettingsLib.BodyMedium"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index c1f254ad2b34..1a043d5015b2 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -187,3 +187,13 @@ flag {
description: "Enable the connection status report for a set of hearing device."
bug: "357882387"
}
+
+flag {
+ name: "ignore_a2dp_disconnection_for_android_auto"
+ namespace: "cross_device_experiences"
+ description: "Do not show problem connecting message when Android Auto disconnect A2DP"
+ bug: "381981752"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume.xml
new file mode 100644
index 000000000000..2c1f300f22ee
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:maxLevel="0" android:drawable="@drawable/ic_ambient_volume_0_0" />
+ <item android:maxLevel="1" android:drawable="@drawable/ic_ambient_volume_0_1" />
+ <item android:maxLevel="2" android:drawable="@drawable/ic_ambient_volume_0_2" />
+ <item android:maxLevel="3" android:drawable="@drawable/ic_ambient_volume_0_3" />
+ <item android:maxLevel="4" android:drawable="@drawable/ic_ambient_volume_0_4" />
+ <item android:maxLevel="5" android:drawable="@drawable/ic_ambient_volume_1_0" />
+ <item android:maxLevel="6" android:drawable="@drawable/ic_ambient_volume_1_1" />
+ <item android:maxLevel="7" android:drawable="@drawable/ic_ambient_volume_1_2" />
+ <item android:maxLevel="8" android:drawable="@drawable/ic_ambient_volume_1_3" />
+ <item android:maxLevel="9" android:drawable="@drawable/ic_ambient_volume_1_4" />
+ <item android:maxLevel="10" android:drawable="@drawable/ic_ambient_volume_2_0" />
+ <item android:maxLevel="11" android:drawable="@drawable/ic_ambient_volume_2_1" />
+ <item android:maxLevel="12" android:drawable="@drawable/ic_ambient_volume_2_2" />
+ <item android:maxLevel="13" android:drawable="@drawable/ic_ambient_volume_2_3" />
+ <item android:maxLevel="14" android:drawable="@drawable/ic_ambient_volume_2_4" />
+ <item android:maxLevel="15" android:drawable="@drawable/ic_ambient_volume_3_0" />
+ <item android:maxLevel="16" android:drawable="@drawable/ic_ambient_volume_3_1" />
+ <item android:maxLevel="17" android:drawable="@drawable/ic_ambient_volume_3_2" />
+ <item android:maxLevel="18" android:drawable="@drawable/ic_ambient_volume_3_3" />
+ <item android:maxLevel="19" android:drawable="@drawable/ic_ambient_volume_3_4" />
+ <item android:maxLevel="20" android:drawable="@drawable/ic_ambient_volume_4_0" />
+ <item android:maxLevel="21" android:drawable="@drawable/ic_ambient_volume_4_1" />
+ <item android:maxLevel="22" android:drawable="@drawable/ic_ambient_volume_4_2" />
+ <item android:maxLevel="23" android:drawable="@drawable/ic_ambient_volume_4_3" />
+ <item android:maxLevel="24" android:drawable="@drawable/ic_ambient_volume_4_4" />
+</level-list> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml
new file mode 100644
index 000000000000..738fe89636c9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M792,904L671,783Q646,799 618,810.5Q590,822 560,829L560,747Q574,742 587.5,737Q601,732 613,725L480,592L480,800L280,600L120,600L120,360L248,360L56,168L112,112L848,848L792,904ZM784,672L726,614Q743,583 751.5,549Q760,515 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,532 825.5,581Q811,630 784,672ZM650,538L560,448L560,318Q607,340 633.5,384Q660,428 660,480Q660,495 657.5,509.5Q655,524 650,538ZM480,368L376,264L480,160L480,368ZM400,606L400,512L328,440L328,440L200,440L200,520L314,520L400,606ZM364,476L364,476L364,476L364,476L364,476L364,476L364,476Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml
new file mode 100644
index 000000000000..8de24f980927
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml
new file mode 100644
index 000000000000..267093ecc9eb
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml
new file mode 100644
index 000000000000..fb21254faf76
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml
new file mode 100644
index 000000000000..4f1e054b0f97
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml
new file mode 100644
index 000000000000..d151fee3e8ab
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml
new file mode 100644
index 000000000000..3e97a2e757f9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml
new file mode 100644
index 000000000000..7bfd662c8261
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml
new file mode 100644
index 000000000000..bba33b348749
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml
new file mode 100644
index 000000000000..c0043166a121
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml
new file mode 100644
index 000000000000..d89ed87f7e40
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml
new file mode 100644
index 000000000000..e0a9c410e74b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml
new file mode 100644
index 000000000000..8627af9ab88f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml
new file mode 100644
index 000000000000..2c1139a3a042
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml
new file mode 100644
index 000000000000..8d920b182286
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml
@@ -0,0 +1,47 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml
new file mode 100644
index 000000000000..7fd4e17e51ca
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.043,0.761L8.814,0.286L9.887,4.192L8.116,4.666L7.043,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.02,3.652L4.317,2.355L7.162,5.201L5.866,6.497L3.02,3.652Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.898,8.942L1.153,7.127L5.163,7.636L4.908,9.451L0.898,8.942Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.589,13.874L1.053,12.121L4.93,10.941L5.466,12.694L1.589,13.874Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml
new file mode 100644
index 000000000000..f0f81506f446
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml
new file mode 100644
index 000000000000..1bb20170bc9b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml
new file mode 100644
index 000000000000..a8bc0af46ccc
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml
@@ -0,0 +1,47 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml
new file mode 100644
index 000000000000..f8a58323e6ba
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.162,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml
new file mode 100644
index 000000000000..e5d7ad420441
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml
new file mode 100644
index 000000000000..f5cdf5d62db4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml
new file mode 100644
index 000000000000..cbed634e4544
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml
new file mode 100644
index 000000000000..90d81d8bd9ec
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.566,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml
new file mode 100644
index 000000000000..f1a9a8a942b0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.975,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.975,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.878,8.868L1.134,7.052L5.163,7.632L4.907,9.447L0.878,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z" android:strokeAlpha="0.4"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ae04ca158e01..9af8ba842a61 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -621,7 +621,7 @@
<string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Kon nie die gedeelde data uitvee nie."</string>
<string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Daar is geen huurkontrakte vir hierdie gedeelde data verkry nie. Wil jy dit uitvee?"</string>
<string name="accessor_info_title" msgid="8289823651512477787">"Programme wat data deel"</string>
- <string name="accessor_no_description_text" msgid="7510967452505591456">"Geen beskrywing is deur die program voorsien nie."</string>
+ <string name="accessor_no_description_text" msgid="7510967452505591456">"Geen beskrywing is deur die app voorsien nie."</string>
<string name="accessor_expires_text" msgid="4625619273236786252">"Huurkontrak verval op <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="delete_blob_text" msgid="2819192607255625697">"Vee gedeelde data uit?"</string>
<string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Is jy seker jy wil hierdie gedeelde data uitvee?"</string>
@@ -737,7 +737,7 @@
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"Verander uitvoer"</string>
<string name="back_navigation_animation" msgid="8105467568421689484">"Voorspellingteruggebaaranimasies"</string>
<string name="back_navigation_animation_summary" msgid="741292224121599456">"Aktiveer stelselanimasies vir voorspellingteruggebaar."</string>
- <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Hierdie instelling aktiveer stelselanimasies vir voorspellinggebaaranimasie. Dit vereis dat enableOnBackInvokedCallback per program op waar gestel word in die manifeslêer."</string>
+ <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Hierdie instelling aktiveer stelselanimasies vir voorspellinggebaaranimasie. Dit vereis dat enableOnBackInvokedCallback per app op waar gestel word in die manifeslêer."</string>
<string name="font_scale_percentage" msgid="2624057443622817886">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="not_specified" msgid="5423502443185110328">"Nie gespesifiseer nie"</string>
<string name="neuter" msgid="2075249330106127310">"Neutrum"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 0b933a33c3f8..cd6cfdfbf1ec 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -239,7 +239,7 @@
<string name="category_work" msgid="4014193632325996115">"Աշխատանքային"</string>
<string name="category_private" msgid="4244892185452788977">"Մասնավոր"</string>
<string name="category_clone" msgid="1554511758987195974">"Կլոն"</string>
- <string name="development_settings_title" msgid="140296922921597393">"Մշակողի ընտրանքներ"</string>
+ <string name="development_settings_title" msgid="140296922921597393">"Ծրագրավորողի ընտրանքներ"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Միացնել մշակողի ընտրանքները"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Կարգավորել ընտրանքները ծրագրի ծրագրավորման համար"</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Ծրագրավորման ընտրանքներն այլևս հասանելի չեն այս օգտատիրոջ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 690add8f0b1c..a85d0cc02216 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -638,7 +638,7 @@
<string name="user_setup_dialog_title" msgid="8037342066381939995">"ಈಗ ಬಳಕೆದಾರರನ್ನು ಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"ಸಾಧನವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ಅದರ ಸ್ಥಳವನ್ನು ಹೊಂದಿಸಲು ವ್ಯಕ್ತಿಯು ಲಭ್ಯವಿದ್ದಾರೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"ಇದೀಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ಹೊಂದಿಸುವುದೇ?"</string>
- <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ಇದೀಗ ಹೊಂದಿಸಿ"</string>
+ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ಇದೀಗ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"ಈಗಲೇ ಬೇಡ"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"ಸೇರಿಸಿ"</string>
<string name="user_new_user_name" msgid="60979820612818840">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
@@ -646,7 +646,7 @@
<string name="user_info_settings_title" msgid="6351390762733279907">"ಬಳಕೆದಾರರ ಮಾಹಿತಿ"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್‌‌ ಮಾಹಿತಿ"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್‌ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
- <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
+ <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 0d27f0012130..894273703dca 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -601,7 +601,7 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ଏଠାରେ ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ସକ୍ରିୟ କରନ୍ତୁ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ଅନୁମୋଦନ କରାଯାଇନାହିଁ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ଏଠାରେ ମିଡିଆ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="tv_media_transfer_connected" msgid="5145011475885290725">"କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"କନେକ୍ଟ ହୋଇଛି"</string>
<string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 504d821a95d9..2676d0ee083c 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -313,7 +313,7 @@
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Bite për shembull"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"Modaliteti i kanalit të audios me Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Modaliteti i kanalit"</string>
- <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Kodeku LDAC i audios së Bluetooth-it: Cilësia e luajtjes"</string>
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Kodeku LDAC i audios me Bluetooth: Cilësia e luajtjes"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Aktivizo LDAC të audios me Bluetooth\nZgjedhja e kodekut: Cilësia e luajtjes"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Transmetimi: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS-ja private"</string>
@@ -586,8 +586,7 @@
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string>
<string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ky kompjuter (i brendshëm)"</string>
- <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) -->
- <skip />
+ <string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"Ky televizor"</string>
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string>
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string>
@@ -607,10 +606,8 @@
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Lidhur përmes ARC-së"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Lidhur përmes eARC-së"</string>
- <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) -->
- <skip />
+ <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="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 6032efbf3ed1..234af3680512 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -257,12 +257,12 @@
<item msgid="1212561935004167943">"Тест. команди малювання зеленим"</item>
</string-array>
<string-array name="track_frame_time_entries">
- <item msgid="634406443901014984">"Вимк."</item>
+ <item msgid="634406443901014984">"Вимкнено"</item>
<item msgid="1288760936356000927">"На екрані у вигляді смужок"</item>
<item msgid="5023908510820531131">"У команді \"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>\""</item>
</string-array>
<string-array name="debug_hw_overdraw_entries">
- <item msgid="1968128556747588800">"Вимк."</item>
+ <item msgid="1968128556747588800">"Вимкнено"</item>
<item msgid="3033215374382962216">"Показувати області накладання"</item>
<item msgid="3474333938380896988">"Показувати області дейтераномалії"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 724899972297..87e03dc7ca19 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -128,7 +128,7 @@
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允许访问通讯录和通话记录"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"信息将用于来电通知等用途"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"这些信息将用于来电通知等用途"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5a4d3ce5661b..3d3dad379417 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -667,4 +667,25 @@
<item>3</item>
</string-array>
+ <!-- Options for showing shade on external display for developers -->
+ <string-array name="shade_display_awareness_entries" >
+ <item>Device display only (Default)</item>
+ <item>External display</item>
+ <item>Focus-based</item>
+ </string-array>
+
+ <!-- Options for showing shade on external display for developers -->
+ <string-array name="shade_display_awareness_summaries" >
+ <item>Show shade on device display only </item>
+ <item>Show device on single external display</item>
+ <item>Show device on last focused display</item>
+ </string-array>
+
+ <!-- Values for showing shade on external display for developers -->
+ <string-array name="shade_display_awareness_values" >
+ <item>default_display</item>
+ <item>any_external_display</item>
+ <item>status_bar_latest_touch</item>
+ </string-array>
+
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index eaf155df4785..e1929b725a58 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -990,6 +990,9 @@
<!-- UI debug setting: simulate secondary display devices using overlays [CHAR LIMIT=45] -->
<string name="overlay_display_devices_title">Simulate secondary displays</string>
+ <!-- UI debug setting: shade display awareness title [CHAR LIMIT=45] -->
+ <string name="shade_display_awareness_title">Shade display position</string>
+
<!-- Preference category for application debugging development settings. [CHAR LIMIT=25] -->
<string name="debug_applications_category">Apps</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java
new file mode 100644
index 000000000000..7f0c1263570e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java
@@ -0,0 +1,415 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import static android.bluetooth.AudioInputControl.MUTE_DISABLED;
+import static android.bluetooth.AudioInputControl.MUTE_MUTED;
+import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;
+
+import static com.android.settingslib.bluetooth.HearingDeviceLocalDataManager.Data.INVALID_VOLUME;
+
+import android.bluetooth.AudioInputControl;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * AmbientVolumeController manages the {@link AudioInputControl}s of
+ * {@link AudioInputControl#AUDIO_INPUT_TYPE_AMBIENT} on the remote device.
+ */
+public class AmbientVolumeController implements LocalBluetoothProfileManager.ServiceListener {
+
+ private static final boolean DEBUG = true;
+ private static final String TAG = "AmbientController";
+
+ private final LocalBluetoothProfileManager mProfileManager;
+ private final VolumeControlProfile mVolumeControlProfile;
+ private final Map<BluetoothDevice, List<AudioInputControl>> mDeviceAmbientControlsMap =
+ new ArrayMap<>();
+ private final Map<BluetoothDevice, AmbientCallback> mDeviceCallbackMap = new ArrayMap<>();
+ final Map<BluetoothDevice, RemoteAmbientState> mDeviceAmbientStateMap =
+ new ArrayMap<>();
+ @Nullable
+ private final AmbientVolumeControlCallback mCallback;
+
+ public AmbientVolumeController(
+ @NonNull LocalBluetoothProfileManager profileManager,
+ @Nullable AmbientVolumeControlCallback callback) {
+ mProfileManager = profileManager;
+ mVolumeControlProfile = profileManager.getVolumeControlProfile();
+ if (mVolumeControlProfile != null && !mVolumeControlProfile.isProfileReady()) {
+ mProfileManager.addServiceListener(this);
+ }
+ mCallback = callback;
+ }
+
+ @Override
+ public void onServiceConnected() {
+ if (mVolumeControlProfile != null && mVolumeControlProfile.isProfileReady()) {
+ mProfileManager.removeServiceListener(this);
+ if (mCallback != null) {
+ mCallback.onVolumeControlServiceConnected();
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected() {
+ // Do nothing
+ }
+
+ /**
+ * Registers the same {@link AmbientCallback} on all ambient control points of the remote
+ * device. The {@link AmbientCallback} will pass the event to registered
+ * {@link AmbientVolumeControlCallback} if exists.
+ *
+ * @param executor the executor to run the callback
+ * @param device the remote device
+ */
+ public void registerCallback(@NonNull Executor executor, @NonNull BluetoothDevice device) {
+ AmbientCallback ambientCallback = new AmbientCallback(device, mCallback);
+ synchronized (mDeviceCallbackMap) {
+ mDeviceCallbackMap.put(device, ambientCallback);
+ }
+
+ // register callback on all ambient input control points of this device
+ List<AudioInputControl> controls = getAmbientControls(device);
+ controls.forEach((control) -> {
+ try {
+ control.registerCallback(executor, ambientCallback);
+ } catch (IllegalArgumentException e) {
+ // The callback was already registered
+ Log.i(TAG, "Skip registering the callback, " + e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Unregisters the {@link AmbientCallback} on all ambient control points of the remote
+ * device which is previously registered with {@link #registerCallback}.
+ *
+ * @param device the remote device
+ */
+ public void unregisterCallback(@NonNull BluetoothDevice device) {
+ AmbientCallback ambientCallback;
+ synchronized (mDeviceCallbackMap) {
+ ambientCallback = mDeviceCallbackMap.remove(device);
+ }
+ if (ambientCallback == null) {
+ // callback not found, no need to unregister
+ return;
+ }
+
+ // unregister callback on all ambient input control points of this device
+ List<AudioInputControl> controls = getAmbientControls(device);
+ controls.forEach(control -> {
+ try {
+ control.unregisterCallback(ambientCallback);
+ } catch (IllegalArgumentException e) {
+ // The callback was never registered or was already unregistered
+ Log.i(TAG, "Skip unregistering the callback, " + e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Gets the gain setting max value from first ambient control point of the remote device.
+ *
+ * @param device the remote device
+ */
+ public int getAmbientMax(@NonNull BluetoothDevice device) {
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ int value = INVALID_VOLUME;
+ if (!ambientControls.isEmpty()) {
+ value = ambientControls.getFirst().getGainSettingMax();
+ }
+ return value;
+ }
+
+ /**
+ * Gets the gain setting min value from first ambient control point of the remote device.
+ *
+ * @param device the remote device
+ */
+ public int getAmbientMin(@NonNull BluetoothDevice device) {
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ int value = INVALID_VOLUME;
+ if (!ambientControls.isEmpty()) {
+ value = ambientControls.getFirst().getGainSettingMin();
+ }
+ return value;
+ }
+
+ /**
+ * Gets the latest values in {@link RemoteAmbientState}.
+ *
+ * @param device the remote device
+ * @return the {@link RemoteAmbientState} represents current remote ambient control point state
+ */
+ @Nullable
+ public RemoteAmbientState refreshAmbientState(@Nullable BluetoothDevice device) {
+ if (device == null || !device.isConnected()) {
+ return null;
+ }
+ int gainSetting = getAmbient(device);
+ int mute = getMute(device);
+ return new RemoteAmbientState(gainSetting, mute);
+ }
+
+ /**
+ * Gets the gain setting value from first ambient control point of the remote device and
+ * stores it in cached {@link RemoteAmbientState}.
+ *
+ * When any audio input point receives {@link AmbientCallback#onGainSettingChanged(int)}
+ * callback, only the changed value which is different from the value stored in the cached
+ * state will be notified to the {@link AmbientVolumeControlCallback} of this controller.
+ *
+ * @param device the remote device
+ */
+ public int getAmbient(@NonNull BluetoothDevice device) {
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ int value = INVALID_VOLUME;
+ if (!ambientControls.isEmpty()) {
+ synchronized (mDeviceAmbientStateMap) {
+ value = ambientControls.getFirst().getGainSetting();
+ RemoteAmbientState state = mDeviceAmbientStateMap.getOrDefault(device,
+ new RemoteAmbientState(INVALID_VOLUME, MUTE_DISABLED));
+ RemoteAmbientState updatedState = new RemoteAmbientState(value, state.mute);
+ mDeviceAmbientStateMap.put(device, updatedState);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Sets the gain setting value to all ambient control points of the remote device.
+ *
+ * @param device the remote device
+ * @param value the gain setting value to be updated
+ */
+ public void setAmbient(@NonNull BluetoothDevice device, int value) {
+ if (DEBUG) {
+ Log.d(TAG, "setAmbient, value:" + value + ", device:" + device);
+ }
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ ambientControls.forEach(control -> control.setGainSetting(value));
+ }
+
+ /**
+ * Gets the mute state from first ambient control point of the remote device and
+ * stores it in cached {@link RemoteAmbientState}. The value will be one of
+ * {@link AudioInputControl.Mute}.
+ *
+ * When any audio input point receives {@link AmbientCallback#onMuteChanged(int)} callback,
+ * only the changed value which is different from the value stored in the cached state will
+ * be notified to the {@link AmbientVolumeControlCallback} of this controller.
+ *
+ * @param device the remote device
+ */
+ public int getMute(@NonNull BluetoothDevice device) {
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ int value = MUTE_DISABLED;
+ if (!ambientControls.isEmpty()) {
+ synchronized (mDeviceAmbientStateMap) {
+ value = ambientControls.getFirst().getMute();
+ RemoteAmbientState state = mDeviceAmbientStateMap.getOrDefault(device,
+ new RemoteAmbientState(INVALID_VOLUME, MUTE_DISABLED));
+ RemoteAmbientState updatedState = new RemoteAmbientState(state.gainSetting, value);
+ mDeviceAmbientStateMap.put(device, updatedState);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Sets the mute state to all ambient control points of the remote device.
+ *
+ * @param device the remote device
+ * @param muted the mute state to be updated
+ */
+ public void setMuted(@NonNull BluetoothDevice device, boolean muted) {
+ if (DEBUG) {
+ Log.d(TAG, "setMuted, muted:" + muted + ", device:" + device);
+ }
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ ambientControls.forEach(control -> {
+ try {
+ control.setMute(muted ? MUTE_MUTED : MUTE_NOT_MUTED);
+ } catch (IllegalStateException e) {
+ // Sometimes remote will throw this exception due to initialization not done
+ // yet. Catch it to prevent crashes on UI.
+ Log.w(TAG, "Remote mute state is currently disabled.");
+ }
+ });
+ }
+
+ /**
+ * Checks if there's any valid ambient control point exists on the remote device
+ *
+ * @param device the remote device
+ */
+ public boolean isAmbientControlAvailable(@NonNull BluetoothDevice device) {
+ final boolean hasAmbientControlPoint = !getAmbientControls(device).isEmpty();
+ final boolean connectedToVcp = mVolumeControlProfile.getConnectionStatus(device)
+ == BluetoothProfile.STATE_CONNECTED;
+ return hasAmbientControlPoint && connectedToVcp;
+ }
+
+ @NonNull
+ private List<AudioInputControl> getAmbientControls(@NonNull BluetoothDevice device) {
+ if (mVolumeControlProfile == null) {
+ return Collections.emptyList();
+ }
+ synchronized (mDeviceAmbientControlsMap) {
+ if (mDeviceAmbientControlsMap.containsKey(device)) {
+ return mDeviceAmbientControlsMap.get(device);
+ }
+ List<AudioInputControl> ambientControls =
+ mVolumeControlProfile.getAudioInputControlServices(device).stream().filter(
+ this::isValidAmbientControl).toList();
+ if (!ambientControls.isEmpty()) {
+ mDeviceAmbientControlsMap.put(device, ambientControls);
+ }
+ return ambientControls;
+ }
+ }
+
+ private boolean isValidAmbientControl(AudioInputControl control) {
+ boolean isAmbientControl =
+ control.getAudioInputType() == AudioInputControl.AUDIO_INPUT_TYPE_AMBIENT;
+ boolean isManual = control.getGainMode() == AudioInputControl.GAIN_MODE_MANUAL
+ || control.getGainMode() == AudioInputControl.GAIN_MODE_MANUAL_ONLY;
+ boolean isActive =
+ control.getAudioInputStatus() == AudioInputControl.AUDIO_INPUT_STATUS_ACTIVE;
+
+ return isAmbientControl && isManual && isActive;
+ }
+
+ /**
+ * Callback providing information about the status and received events of
+ * {@link AmbientVolumeController}.
+ */
+ public interface AmbientVolumeControlCallback {
+
+ /** This method is called when the Volume Control Service is connected */
+ default void onVolumeControlServiceConnected() {
+ }
+
+ /**
+ * This method is called when one of the remote device's ambient control point's gain
+ * settings value is changed.
+ *
+ * @param device the remote device
+ * @param gainSettings the new gain setting value
+ */
+ default void onAmbientChanged(@NonNull BluetoothDevice device, int gainSettings) {
+ }
+
+ /**
+ * This method is called when one of the remote device's ambient control point's mute
+ * state is changed.
+ *
+ * @param device the remote device
+ * @param mute the new mute state
+ */
+ default void onMuteChanged(@NonNull BluetoothDevice device, int mute) {
+ }
+
+ /**
+ * This method is called when any command to the remote device's ambient control point
+ * is failed.
+ *
+ * @param device the remote device.
+ */
+ default void onCommandFailed(@NonNull BluetoothDevice device) {
+ }
+ }
+
+ /**
+ * A wrapper callback that will pass {@link AudioInputControl.AudioInputCallback} with extra
+ * device information to {@link AmbientVolumeControlCallback}.
+ */
+ class AmbientCallback implements AudioInputControl.AudioInputCallback {
+
+ private final BluetoothDevice mDevice;
+ private final AmbientVolumeControlCallback mCallback;
+
+ AmbientCallback(@NonNull BluetoothDevice device,
+ @Nullable AmbientVolumeControlCallback callback) {
+ mDevice = device;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onGainSettingChanged(int gainSetting) {
+ if (mCallback != null) {
+ synchronized (mDeviceAmbientStateMap) {
+ RemoteAmbientState previousState = mDeviceAmbientStateMap.get(mDevice);
+ if (previousState.gainSetting != gainSetting) {
+ mCallback.onAmbientChanged(mDevice, gainSetting);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSetGainSettingFailed() {
+ Log.w(TAG, "onSetGainSettingFailed, device=" + mDevice);
+ if (mCallback != null) {
+ mCallback.onCommandFailed(mDevice);
+ }
+ }
+
+ @Override
+ public void onMuteChanged(int mute) {
+ if (mCallback != null) {
+ synchronized (mDeviceAmbientStateMap) {
+ RemoteAmbientState previousState = mDeviceAmbientStateMap.get(mDevice);
+ if (previousState.mute != mute) {
+ mCallback.onMuteChanged(mDevice, mute);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSetMuteFailed() {
+ Log.w(TAG, "onSetMuteFailed, device=" + mDevice);
+ if (mCallback != null) {
+ mCallback.onCommandFailed(mDevice);
+ }
+ }
+ }
+
+ public record RemoteAmbientState(int gainSetting, int mute) {
+ public boolean isMutable() {
+ return mute != MUTE_DISABLED;
+ }
+ public boolean isMuted() {
+ return mute == MUTE_MUTED;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 216574a5fff9..429e4c958f05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -631,15 +631,15 @@ public class BluetoothUtils {
assistantProfile.getAllConnectedDevices().stream()
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
- .map(CachedBluetoothDevice::getGroupId)
+ .map(BluetoothUtils::getGroupId)
.collect(Collectors.toSet());
Set<Integer> activeGroupIds =
leAudioProfile.getActiveDevices().stream()
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
- .map(CachedBluetoothDevice::getGroupId)
+ .map(BluetoothUtils::getGroupId)
.collect(Collectors.toSet());
- int groupId = cachedDevice.getGroupId();
+ int groupId = getGroupId(cachedDevice);
return activeGroupIds.size() == 1
&& !activeGroupIds.contains(groupId)
&& connectedGroupIds.size() == 2
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4eb0567c67d9..b4afb7d8cd4c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -17,6 +17,7 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.flags.Flags.enableSetPreferredTransportForLeAudioDevice;
+import static com.android.settingslib.flags.Flags.ignoreA2dpDisconnectionForAndroidAuto;
import android.annotation.CallbackExecutor;
import android.annotation.StringRes;
@@ -82,6 +83,8 @@ import java.util.stream.Stream;
*/
public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private static final String TAG = "CachedBluetoothDevice";
+ private static final ParcelUuid ANDROID_AUTO_UUID =
+ ParcelUuid.fromString("4de17a00-52cb-11e6-bdf4-0800200c9a66");
// See mConnectAttempted
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
@@ -260,18 +263,26 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
if (mHandler.hasMessages(profile.getProfileId())) {
mHandler.removeMessages(profile.getProfileId());
if (profile.getConnectionPolicy(mDevice) >
- BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
- /*
- * If we received state DISCONNECTED and previous state was
- * CONNECTING and connection policy is FORBIDDEN or UNKNOWN
- * then it's not really a failure to connect.
- *
- * Connection profile is considered as failed when connection
- * policy indicates that profile should be connected
- * but it got disconnected.
- */
- Log.w(TAG, "onProfileStateChanged(): Failed to connect profile");
- setProfileConnectedStatus(profile.getProfileId(), true);
+ BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+ if (ignoreA2dpDisconnectionForAndroidAuto()
+ && profile instanceof A2dpProfile && isAndroidAuto()) {
+ Log.w(TAG,
+ "onProfileStateChanged(): Skip setting A2DP "
+ + "connection fail for Android Auto");
+ } else {
+ /*
+ * If we received state DISCONNECTED and previous state was
+ * CONNECTING and connection policy is FORBIDDEN or UNKNOWN
+ * then it's not really a failure to connect.
+ *
+ * Connection profile is considered as failed when connection
+ * policy indicates that profile should be connected
+ * but it got disconnected.
+ */
+ Log.w(TAG,
+ "onProfileStateChanged(): Failed to connect profile");
+ setProfileConnectedStatus(profile.getProfileId(), true);
+ }
}
}
break;
@@ -1580,6 +1591,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private int getHearingDeviceSummaryRes(int leftBattery, int rightBattery,
boolean shortSummary) {
+ if (getDeviceSide() == HearingAidInfo.DeviceSide.SIDE_MONO
+ || getDeviceSide() == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT) {
+ return !shortSummary && (getBatteryLevel() > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ ? R.string.bluetooth_active_battery_level
+ : R.string.bluetooth_active_no_battery_level;
+ }
boolean isLeftDeviceConnected = getConnectedHearingAidSide(
HearingAidInfo.DeviceSide.SIDE_LEFT).isPresent();
boolean isRightDeviceConnected = getConnectedHearingAidSide(
@@ -1635,8 +1652,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
@HearingAidInfo.DeviceSide int side) {
return Stream.concat(Stream.of(this, mSubDevice), mMemberDevices.stream())
.filter(Objects::nonNull)
- .filter(device -> device.getDeviceSide() == side
- || device.getDeviceSide() == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT)
+ .filter(device -> device.getDeviceSide() == side)
.filter(device -> device.getDevice().isConnected())
// For hearing aids, we should expect only one device assign to one side, but if
// it happens, we don't care which one.
@@ -1900,6 +1916,25 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
BluetoothProfile.STATE_CONNECTED;
}
+ /**
+ * @return {@code true} if {@code cachedBluetoothDevice} supports broadcast assistant profile
+ */
+ public boolean isConnectedLeAudioBroadcastAssistantDevice() {
+ LocalBluetoothLeBroadcastAssistant leBroadcastAssistant =
+ mProfileManager.getLeAudioBroadcastAssistantProfile();
+ return leBroadcastAssistant != null && leBroadcastAssistant.getConnectionStatus(mDevice)
+ == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ /**
+ * @return {@code true} if {@code cachedBluetoothDevice} supports volume control profile
+ */
+ public boolean isConnectedVolumeControlDevice() {
+ VolumeControlProfile volumeControl = mProfileManager.getVolumeControlProfile();
+ return volumeControl != null && volumeControl.getConnectionStatus(mDevice)
+ == BluetoothProfile.STATE_CONNECTED;
+ }
+
private boolean isConnectedSapDevice() {
SapProfile sapProfile = mProfileManager.getSapProfile();
return sapProfile != null && sapProfile.getConnectionStatus(mDevice)
@@ -2031,4 +2066,16 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
void setLocalBluetoothManager(LocalBluetoothManager bluetoothManager) {
mBluetoothManager = bluetoothManager;
}
+
+ private boolean isAndroidAuto() {
+ try {
+ ParcelUuid[] uuids = mDevice.getUuids();
+ if (ArrayUtils.contains(uuids, ANDROID_AUTO_UUID)) {
+ return true;
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Fail to check isAndroidAuto for " + this);
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 7fdb32cb63e9..b754706fb9a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -153,7 +153,7 @@ public class CachedBluetoothDeviceManager {
/**
* Returns device summary of the pair of the hearing aid / CSIP passed as the parameter.
*
- * @param CachedBluetoothDevice device
+ * @param device the remote device
* @return Device summary, or if the pair does not exist or if it is not a hearing aid or
* a CSIP set member, then {@code null}.
*/
@@ -394,6 +394,7 @@ public class CachedBluetoothDeviceManager {
}
public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
+ mHearingAidDeviceManager.clearLocalDataIfNeeded(device);
device.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
// Should iterate through the cloned set to avoid ConcurrentModificationException
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index fa28cf6c8a76..1ca4c2b39a70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.le.ScanFilter;
@@ -308,6 +307,10 @@ public class HearingAidDeviceManager {
}
}
+ void clearLocalDataIfNeeded(CachedBluetoothDevice device) {
+ HearingDeviceLocalDataManager.clear(mContext, device.getDevice());
+ }
+
private void setAudioRoutingConfig(CachedBluetoothDevice device) {
AudioDeviceAttributes hearingDeviceAttributes =
mRoutingHelper.getMatchedHearingDeviceAttributes(device);
@@ -428,8 +431,7 @@ public class HearingAidDeviceManager {
p -> p instanceof HapClientProfile)) {
int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
- if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
- && hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
+ if (hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
final HearingAidInfo info = new HearingAidInfo.Builder()
.setLeAudioLocation(audioLocation)
.setHapDeviceType(hearingAidType)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidInfo.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidInfo.java
index ef08c924b844..8399824f11e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidInfo.java
@@ -34,6 +34,7 @@ public class HearingAidInfo {
DeviceSide.SIDE_LEFT,
DeviceSide.SIDE_RIGHT,
DeviceSide.SIDE_LEFT_AND_RIGHT,
+ DeviceSide.SIDE_MONO
})
/** Side definition for hearing aids. */
@@ -42,6 +43,7 @@ public class HearingAidInfo {
int SIDE_LEFT = 0;
int SIDE_RIGHT = 1;
int SIDE_LEFT_AND_RIGHT = 2;
+ int SIDE_MONO = 3;
}
@Retention(java.lang.annotation.RetentionPolicy.SOURCE)
@@ -124,6 +126,9 @@ public class HearingAidInfo {
@DeviceSide
private static int convertLeAudioLocationToInternalSide(int leAudioLocation) {
+ if (leAudioLocation == BluetoothLeAudio.AUDIO_LOCATION_MONO) {
+ return DeviceSide.SIDE_MONO;
+ }
boolean isLeft = (leAudioLocation & LE_AUDIO_LOCATION_LEFT) != 0;
boolean isRight = (leAudioLocation & LE_AUDIO_LOCATION_RIGHT) != 0;
if (isLeft && isRight) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
index 7a64965334c9..6725558cd2bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
@@ -86,6 +86,17 @@ public class HearingDeviceLocalDataManager {
mSettingsObserver = new SettingsObserver(ThreadUtils.getUiThreadHandler());
}
+ /**
+ * Clears the local data of the device. This method should be called when the device is
+ * unpaired.
+ */
+ public static void clear(@NonNull Context context, @NonNull BluetoothDevice device) {
+ HearingDeviceLocalDataManager manager = new HearingDeviceLocalDataManager(context);
+ manager.getLocalDataFromSettings();
+ manager.remove(device);
+ manager.putAmbientVolumeSettings();
+ }
+
/** Starts the manager. Loads the data from Settings and start observing any changes. */
public synchronized void start() {
if (mIsStarted) {
@@ -141,6 +152,7 @@ public class HearingDeviceLocalDataManager {
* Puts the local data of the corresponding hearing device.
*
* @param device the device to update the local data
+ * @param data the local data to be stored
*/
private void put(BluetoothDevice device, Data data) {
if (device == null) {
@@ -148,7 +160,11 @@ public class HearingDeviceLocalDataManager {
}
synchronized (sLock) {
final String addr = device.getAnonymizedAddress();
- mAddrToDataMap.put(addr, data);
+ if (data == null) {
+ mAddrToDataMap.remove(addr);
+ } else {
+ mAddrToDataMap.put(addr, data);
+ }
if (mListener != null && mListenerExecutor != null) {
mListenerExecutor.execute(() -> mListener.onDeviceLocalDataChange(addr, data));
}
@@ -156,6 +172,24 @@ public class HearingDeviceLocalDataManager {
}
/**
+ * Removes the local data of the corresponding hearing device.
+ *
+ * @param device the device to remove the local data
+ */
+ private void remove(BluetoothDevice device) {
+ if (device == null) {
+ return;
+ }
+ synchronized (sLock) {
+ final String addr = device.getAnonymizedAddress();
+ mAddrToDataMap.remove(addr);
+ if (mListener != null && mListenerExecutor != null) {
+ mListenerExecutor.execute(() -> mListener.onDeviceLocalDataChange(addr, null));
+ }
+ }
+ }
+
+ /**
* Updates the ambient volume of the corresponding hearing device. This should be called after
* {@link #start()} is called().
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index a4c5a00dc53e..5bcdcc09206b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -32,7 +32,9 @@ import android.content.Context;
import android.os.Build;
import android.util.Log;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
@@ -372,6 +374,27 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile
}
/**
+ * Gets the {@link BluetoothLeBroadcastMetadata} of a specified source added to this sink.
+ *
+ * @param sink Broadcast Sink device
+ * @param sourceId Broadcast source id
+ * @return metadata {@link BluetoothLeBroadcastMetadata} associated with the specified source.
+ */
+ public @Nullable BluetoothLeBroadcastMetadata getSourceMetadata(
+ @NonNull BluetoothDevice sink, @IntRange(from = 0x00, to = 0xFF) int sourceId) {
+ if (mService == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
+ return null;
+ }
+ try {
+ return mService.getSourceMetadata(sink, sourceId);
+ } catch (IllegalArgumentException | NoSuchMethodError e) {
+ Log.w(TAG, "Error calling getSourceMetadata()", e);
+ }
+ return null;
+ }
+
+ /**
* Register Broadcast Assistant Callbacks to track its state and receivers
*
* @param executor Executor object for callback
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 76aa5bf3334c..478a5d198239 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -80,8 +80,14 @@ public final class InputRouteManager {
// behavior.
@AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType;
for (AudioDeviceInfo info : addedDevices) {
- if (InputMediaDevice.isSupportedInputDevice(info.getType())) {
- deviceTypeToActivate = info.getType();
+ @AudioDeviceType int type = info.getType();
+ // Since onAudioDevicesAdded is called not only when new device is hot
+ // plugged, but also when the switcher dialog is opened, make sure to check
+ // against existing device list and only activate if the device does not
+ // exist previously.
+ if (InputMediaDevice.isSupportedInputDevice(type)
+ && findDeviceByType(type) == null) {
+ deviceTypeToActivate = type;
}
}
@@ -140,16 +146,22 @@ public final class InputRouteManager {
}
// TODO(b/355684672): handle edge case where there are two devices with the same type. Only
- // using a single mSelectedInputDeviceType might not be enough to recognize the correct device.
- public @Nullable MediaDevice getSelectedInputDevice() {
+ // using a single type might not be enough to recognize the correct device.
+ @Nullable
+ private MediaDevice findDeviceByType(@AudioDeviceType int type) {
for (MediaDevice device : mInputMediaDevices) {
- if (((InputMediaDevice) device).getAudioDeviceInfoType() == mSelectedInputDeviceType) {
+ if (((InputMediaDevice) device).getAudioDeviceInfoType() == type) {
return device;
}
}
return null;
}
+ @Nullable
+ public MediaDevice getSelectedInputDevice() {
+ return findDeviceByType(mSelectedInputDeviceType);
+ }
+
private void applyDefaultSelectedTypeToAllPresets() {
mSelectedInputDeviceType = retrieveDefaultSelectedDeviceType();
AudioDeviceAttributes deviceAttributes =
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 35e3dd3379f0..e1be1d21a9b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -231,7 +231,7 @@ public class MobileStatusTracker {
public SignalStrength signalStrength;
public TelephonyDisplayInfo telephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
/**
* Empty constructor
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index a7e04640d069..e01f27964733 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -27,6 +27,7 @@ import android.net.wifi.WifiManager
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
import android.os.Bundle
import android.os.SystemClock
+import android.security.advancedprotection.AdvancedProtectionManager
import android.util.Log
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
@@ -498,7 +499,13 @@ open class WifiUtils {
): Job =
coroutineScope.launch {
val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
- if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
+ val aapmManager = context.getSystemService(AdvancedProtectionManager::class.java)
+ if (isAdvancedProtectionEnabled(aapmManager)) {
+ val intent = aapmManager.createSupportIntent(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP,
+ AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION)
+ onStartActivity(intent)
+ } else if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
onAllowed()
} else {
val intent = Intent(Intent.ACTION_MAIN).apply {
@@ -522,6 +529,18 @@ open class WifiUtils {
}
}
+ private suspend fun isAdvancedProtectionEnabled(
+ aapmManager: AdvancedProtectionManager?
+ ): Boolean =
+ if (android.security.Flags.aapmApi() &&
+ com.android.wifi.flags.Flags.wepDisabledInApm() &&
+ aapmManager != null
+ ) {
+ withContext(Dispatchers.Default) { aapmManager.isAdvancedProtectionEnabled() }
+ } else {
+ false
+ }
+
const val SSID = "ssid"
const val DIALOG_WINDOW_TYPE = "dialog_window_type"
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index a03977c16f66..785bcbf5a91c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -21,15 +21,26 @@ import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NO
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.Authority;
+import android.app.admin.DeviceAdminAuthority;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DpcAuthority;
+import android.app.admin.EnforcingAdmin;
+import android.app.admin.RoleAuthority;
+import android.app.admin.UnknownAuthority;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -37,8 +48,13 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
+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 org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -52,6 +68,8 @@ import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private Context mContext;
@@ -66,6 +84,7 @@ public class RestrictedLockUtilsTest {
private final int mUserId = 194;
private final int mProfileId = 160;
+ private final String mPackage = "test.pkg";
private final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
private final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
@@ -85,6 +104,7 @@ public class RestrictedLockUtilsTest {
RestrictedLockUtilsInternal.sProxy = mProxy;
}
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_AAPM_API)
@Test
public void checkIfRestrictionEnforced_deviceOwner()
throws PackageManager.NameNotFoundException {
@@ -109,6 +129,7 @@ public class RestrictedLockUtilsTest {
assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
}
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_AAPM_API)
@Test
public void checkIfRestrictionEnforced_profileOwner()
throws PackageManager.NameNotFoundException {
@@ -133,6 +154,125 @@ public class RestrictedLockUtilsTest {
assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
}
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void checkIfRestrictionEnforced_getEnforcingAdminExists() {
+ UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
+ UserManager.RESTRICTION_SOURCE_PROFILE_OWNER);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+ final EnforcingAdmin enforcingAdmin = new EnforcingAdmin(mPackage,
+ UnknownAuthority.UNKNOWN_AUTHORITY, UserHandle.of(mUserId), mAdmin1);
+
+ when(mUserManager.getUserRestrictionSources(userRestriction,
+ UserHandle.of(mUserId)))
+ .thenReturn(Collections.singletonList(enforcingUser));
+ when(mDevicePolicyManager.getEnforcingAdmin(mUserId, userRestriction))
+ .thenReturn(enforcingAdmin);
+
+ EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ mContext, userRestriction, mUserId);
+
+ assertThat(enforcedAdmin).isNotNull();
+ assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
+ assertThat(enforcedAdmin.component).isEqualTo(enforcingAdmin.getComponentName());
+ assertThat(enforcedAdmin.user).isEqualTo(enforcingAdmin.getUserHandle());
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void checkIfRestrictionEnforced_getEnforcingAdminReturnsNull_deviceOwner()
+ throws PackageManager.NameNotFoundException {
+ UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
+ UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+
+ when(mUserManager.getUserRestrictionSources(userRestriction,
+ UserHandle.of(mUserId)))
+ .thenReturn(Collections.singletonList(enforcingUser));
+ when(mDevicePolicyManager.getEnforcingAdmin(mUserId, userRestriction))
+ .thenReturn(null);
+ when(mContext.createPackageContextAsUser(any(), eq(0),
+ eq(UserHandle.of(mUserId))))
+ .thenReturn(mContext);
+
+ setUpDeviceOwner(mAdmin1, mUserId);
+
+ EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
+ .checkIfRestrictionEnforced(mContext, userRestriction, mUserId);
+
+ assertThat(enforcedAdmin).isNotNull();
+ assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
+ assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void checkIfRestrictionEnforced_getEnforcingAdminReturnsNull_profileOwner()
+ throws PackageManager.NameNotFoundException {
+ UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
+ UserManager.RESTRICTION_SOURCE_PROFILE_OWNER);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+
+ when(mUserManager.getUserRestrictionSources(userRestriction,
+ UserHandle.of(mUserId)))
+ .thenReturn(Collections.singletonList(enforcingUser));
+ when(mDevicePolicyManager.getEnforcingAdmin(mUserId, userRestriction))
+ .thenReturn(null);
+ when(mContext.createPackageContextAsUser(any(), eq(0),
+ eq(UserHandle.of(mUserId))))
+ .thenReturn(mContext);
+
+ setUpProfileOwner(mAdmin1);
+
+ EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
+ .checkIfRestrictionEnforced(mContext, userRestriction, mUserId);
+
+ assertThat(enforcedAdmin).isNotNull();
+ assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
+ assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void isPolicyEnforcedByAdvancedProtection_notEnforced_returnsFalse() {
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+ final Authority[] allNonAdvancedProtectionAuthorities = new Authority[] {
+ UnknownAuthority.UNKNOWN_AUTHORITY,
+ DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY,
+ DpcAuthority.DPC_AUTHORITY,
+ new RoleAuthority(Collections.singleton("some-role"))
+ };
+
+ for (Authority authority : allNonAdvancedProtectionAuthorities) {
+ final EnforcingAdmin enforcingAdmin = new EnforcingAdmin(mPackage, authority,
+ UserHandle.of(mUserId), mAdmin1);
+
+ when(mDevicePolicyManager.getEnforcingAdmin(mUserId, userRestriction))
+ .thenReturn(enforcingAdmin);
+
+ assertWithMessage(authority + " is not an advanced protection authority")
+ .that(RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(
+ mContext, userRestriction, mUserId))
+ .isFalse();
+ }
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void isPolicyEnforcedByAdvancedProtection_enforced_returnsTrue() {
+ final Authority advancedProtectionAuthority = new UnknownAuthority(
+ ADVANCED_PROTECTION_SYSTEM_ENTITY);
+ final EnforcingAdmin advancedProtectionEnforcingAdmin = new EnforcingAdmin(mPackage,
+ advancedProtectionAuthority, UserHandle.of(mUserId), mAdmin1);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+
+ when(mDevicePolicyManager.getEnforcingAdmin(mUserId, userRestriction))
+ .thenReturn(advancedProtectionEnforcingAdmin);
+
+ assertThat(RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(mContext,
+ userRestriction, mUserId)).isTrue();
+ }
+
@Test
public void checkIfDevicePolicyServiceDisabled_noEnforceAdminForManagedProfile() {
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 7ad54e187ae5..dbbbd5bf8089 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -16,7 +16,10 @@
package com.android.settingslib;
+import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
+
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -26,10 +29,23 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.Authority;
+import android.app.admin.DeviceAdminAuthority;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
+import android.app.admin.DpcAuthority;
+import android.app.admin.EnforcingAdmin;
+import android.app.admin.RoleAuthority;
+import android.app.admin.UnknownAuthority;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
+import android.os.UserManager;
+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.View;
import android.widget.TextView;
@@ -37,14 +53,19 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.Collections;
+
@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private Context mContext;
@@ -57,6 +78,11 @@ public class RestrictedPreferenceHelperTest {
@Mock
private RestrictedTopLevelPreference mRestrictedTopLevelPreference;
+ private final String mPackage = "test.pkg";
+ private final ComponentName mAdmin = new ComponentName("admin", "adminclass");
+ private final Authority mAdvancedProtectionAuthority = new UnknownAuthority(
+ ADVANCED_PROTECTION_SYSTEM_ENTITY);
+
private PreferenceViewHolder mViewHolder;
private RestrictedPreferenceHelper mHelper;
@@ -71,6 +97,7 @@ public class RestrictedPreferenceHelperTest {
mHelper = new RestrictedPreferenceHelper(mContext, mPreference, null);
}
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_AAPM_API)
@Test
public void bindPreference_disabled_shouldDisplayDisabledSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
@@ -101,6 +128,57 @@ public class RestrictedPreferenceHelperTest {
verify(summaryView, never()).setVisibility(View.GONE);
}
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void bindPreference_disabled_byAdvancedProtection_shouldDisplayDisabledSummary() {
+ final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+ final RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils
+ .EnforcedAdmin(/* component */ null, userRestriction, UserHandle.of(
+ UserHandle.myUserId()));
+ final EnforcingAdmin advancedProtectionEnforcingAdmin = new EnforcingAdmin(mPackage,
+ mAdvancedProtectionAuthority, UserHandle.of(UserHandle.myUserId()), mAdmin);
+
+ when(mViewHolder.itemView.findViewById(android.R.id.summary))
+ .thenReturn(summaryView);
+ when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction))
+ .thenReturn(advancedProtectionEnforcingAdmin);
+ when(mContext.getString(
+ com.android.settingslib.widget.restricted.R.string.disabled_by_advanced_protection))
+ .thenReturn("advanced_protection");
+
+ mHelper.useAdminDisabledSummary(true);
+ mHelper.setDisabledByAdmin(enforcedAdmin);
+ mHelper.onBindViewHolder(mViewHolder);
+
+ verify(summaryView).setText("advanced_protection");
+ verify(summaryView, never()).setVisibility(View.GONE);
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void bindPreference_disabled_byAdmin_shouldDisplayDisabledSummary() {
+ final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
+ final EnforcingAdmin nonAdvancedProtectionEnforcingAdmin = new EnforcingAdmin(mPackage,
+ UnknownAuthority.UNKNOWN_AUTHORITY, UserHandle.of(UserHandle.myUserId()), mAdmin);
+ final String userRestriction = UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY;
+
+ when(mViewHolder.itemView.findViewById(android.R.id.summary))
+ .thenReturn(summaryView);
+ when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction))
+ .thenReturn(nonAdvancedProtectionEnforcingAdmin);
+ when(mContext.getString(R.string.disabled_by_admin_summary_text))
+ .thenReturn("test");
+ when(mDevicePolicyResourcesManager.getString(any(), any())).thenReturn("test");
+
+ mHelper.useAdminDisabledSummary(true);
+ mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
+ mHelper.onBindViewHolder(mViewHolder);
+
+ verify(summaryView).setText("test");
+ verify(summaryView, never()).setVisibility(View.GONE);
+ }
+
@Test
public void bindPreference_notDisabled_shouldNotHideSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
@@ -157,4 +235,74 @@ public class RestrictedPreferenceHelperTest {
assertThat(mHelper.isDisabledByAdmin()).isTrue();
}
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void setDisabledByAdmin_previousAndCurrentAdminsAreTheSame_returnsFalse() {
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
+ new RestrictedLockUtils.EnforcedAdmin(/* component */ null,
+ /* enforcedRestriction */ "some_restriction", /* userHandle */ null);
+
+ mHelper.setDisabledByAdmin(enforcedAdmin);
+
+ assertThat(mHelper.setDisabledByAdmin(enforcedAdmin)).isFalse();
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void setDisabledByAdmin_previousAndCurrentAdminsAreDifferent_returnsTrue() {
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin1 =
+ new RestrictedLockUtils.EnforcedAdmin(/* component */ null,
+ /* enforcedRestriction */ "some_restriction", /* userHandle */ null);
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin2 =
+ new RestrictedLockUtils.EnforcedAdmin(new ComponentName("pkg", "cls"),
+ /* enforcedRestriction */ "some_restriction", /* userHandle */ null);
+
+ mHelper.setDisabledByAdmin(enforcedAdmin1);
+
+ assertThat(mHelper.setDisabledByAdmin(enforcedAdmin2)).isTrue();
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void isRestrictionEnforcedByAdvancedProtection_notEnforced_returnsFalse() {
+ final Authority[] allNonAdvancedProtectionAuthorities = new Authority[] {
+ UnknownAuthority.UNKNOWN_AUTHORITY,
+ DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY,
+ DpcAuthority.DPC_AUTHORITY,
+ new RoleAuthority(Collections.singleton("some-role"))
+ };
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+
+ for (Authority authority : allNonAdvancedProtectionAuthorities) {
+ final EnforcingAdmin enforcingAdmin = new EnforcingAdmin(mPackage, authority,
+ UserHandle.of(UserHandle.myUserId()), mAdmin);
+
+ when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction))
+ .thenReturn(enforcingAdmin);
+
+ mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin(/* component */ null,
+ userRestriction, UserHandle.of(UserHandle.myUserId())));
+
+ assertWithMessage(authority + " is not an advanced protection authority")
+ .that(mHelper.isRestrictionEnforcedByAdvancedProtection())
+ .isFalse();
+ }
+ }
+
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
+ @Test
+ public void isRestrictionEnforcedByAdvancedProtection_enforced_returnsTrue() {
+ final EnforcingAdmin advancedProtectionEnforcingAdmin = new EnforcingAdmin(mPackage,
+ mAdvancedProtectionAuthority, UserHandle.of(UserHandle.myUserId()), mAdmin);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+
+ when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction))
+ .thenReturn(advancedProtectionEnforcingAdmin);
+
+ mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin(/* component */ null,
+ userRestriction, UserHandle.of(UserHandle.myUserId())));
+
+ assertThat(mHelper.isRestrictionEnforcedByAdvancedProtection()).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
new file mode 100644
index 000000000000..abc1d226972b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.settingslib.bluetooth;
+
+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.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.AudioInputControl;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** Tests for {@link AmbientVolumeController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AmbientVolumeControllerTest {
+
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private static final String TEST_ADDRESS = "00:00:00:00:11";
+
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private VolumeControlProfile mVolumeControlProfile;
+ @Mock
+ private AmbientVolumeController.AmbientVolumeControlCallback mCallback;
+ @Mock
+ private BluetoothDevice mDevice;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private AmbientVolumeController mVolumeController;
+
+ @Before
+ public void setUp() {
+ when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
+ when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ when(mDevice.isConnected()).thenReturn(true);
+ mVolumeController = new AmbientVolumeController(mProfileManager, mCallback);
+ }
+
+ @Test
+ public void onServiceConnected_notifyCallback() {
+ when(mVolumeControlProfile.isProfileReady()).thenReturn(true);
+
+ mVolumeController.onServiceConnected();
+
+ verify(mCallback).onVolumeControlServiceConnected();
+ }
+
+ @Test
+ public void isAmbientControlAvailable_validControls_assertTrue() {
+ prepareValidAmbientControls();
+
+ assertThat(mVolumeController.isAmbientControlAvailable(mDevice)).isTrue();
+ }
+
+ @Test
+ public void isAmbientControlAvailable_streamingControls_assertFalse() {
+ prepareStreamingControls();
+
+ assertThat(mVolumeController.isAmbientControlAvailable(mDevice)).isFalse();
+ }
+
+ @Test
+ public void isAmbientControlAvailable_automaticAmbientControls_assertFalse() {
+ prepareAutomaticAmbientControls();
+
+ assertThat(mVolumeController.isAmbientControlAvailable(mDevice)).isFalse();
+ }
+
+ @Test
+ public void isAmbientControlAvailable_inactiveAmbientControls_assertFalse() {
+ prepareInactiveAmbientControls();
+
+ assertThat(mVolumeController.isAmbientControlAvailable(mDevice)).isFalse();
+ }
+
+ @Test
+ public void registerCallback_verifyRegisterOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.registerCallback(mContext.getMainExecutor(), mDevice);
+
+ for (AudioInputControl control : controls) {
+ verify(control).registerCallback(any(Executor.class), any());
+ }
+ }
+
+ @Test
+ public void unregisterCallback_verifyUnregisterOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.registerCallback(mContext.getMainExecutor(), mDevice);
+ mVolumeController.unregisterCallback(mDevice);
+
+ for (AudioInputControl control : controls) {
+ verify(control).unregisterCallback(any());
+ }
+ }
+
+ @Test
+ public void getAmbientMax_verifyGetOnFirstControl() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.getAmbientMax(mDevice);
+
+ verify(controls.getFirst()).getGainSettingMax();
+ }
+
+ @Test
+ public void getAmbientMin_verifyGetOnFirstControl() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.getAmbientMin(mDevice);
+
+ verify(controls.getFirst()).getGainSettingMin();
+ }
+
+ @Test
+ public void getAmbient_verifyGetOnFirstControl() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.getAmbient(mDevice);
+
+ verify(controls.getFirst()).getGainSetting();
+ }
+
+ @Test
+ public void setAmbient_verifySetOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.setAmbient(mDevice, 10);
+
+ for (AudioInputControl control : controls) {
+ verify(control).setGainSetting(10);
+ }
+ }
+
+ @Test
+ public void getMute_verifyGetOnFirstControl() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.getMute(mDevice);
+
+ verify(controls.getFirst()).getMute();
+ }
+
+ @Test
+ public void setMuted_true_verifySetOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.setMuted(mDevice, true);
+
+ for (AudioInputControl control : controls) {
+ verify(control).setMute(AudioInputControl.MUTE_MUTED);
+ }
+ }
+
+ @Test
+ public void setMuted_false_verifySetOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.setMuted(mDevice, false);
+
+ for (AudioInputControl control : controls) {
+ verify(control).setMute(AudioInputControl.MUTE_NOT_MUTED);
+ }
+ }
+
+ @Test
+ public void ambientCallback_onGainSettingChanged_verifyCallbackIsCalledWhenStateChange() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+ final int testAmbient = 10;
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+ when(controls.getFirst().getGainSetting()).thenReturn(testAmbient);
+
+ mVolumeController.refreshAmbientState(mDevice);
+ ambientCallback.onGainSettingChanged(testAmbient);
+ verify(mCallback, never()).onAmbientChanged(mDevice, testAmbient);
+
+ final int updatedTestAmbient = 20;
+ ambientCallback.onGainSettingChanged(updatedTestAmbient);
+ verify(mCallback).onAmbientChanged(mDevice, updatedTestAmbient);
+ }
+
+
+ @Test
+ public void ambientCallback_onSetAmbientFailed_verifyCallbackIsCalled() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+
+ ambientCallback.onSetGainSettingFailed();
+
+ verify(mCallback).onCommandFailed(mDevice);
+ }
+
+ @Test
+ public void ambientCallback_onMuteChanged_verifyCallbackIsCalledWhenStateChange() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+ final int testMute = 0;
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+ when(controls.getFirst().getMute()).thenReturn(testMute);
+
+ mVolumeController.refreshAmbientState(mDevice);
+ ambientCallback.onMuteChanged(testMute);
+ verify(mCallback, never()).onMuteChanged(mDevice, testMute);
+
+ final int updatedTestMute = 1;
+ ambientCallback.onMuteChanged(updatedTestMute);
+ verify(mCallback).onMuteChanged(mDevice, updatedTestMute);
+ }
+
+ @Test
+ public void ambientCallback_onSetMuteFailed_verifyCallbackIsCalled() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+
+ ambientCallback.onSetMuteFailed();
+
+ verify(mCallback).onCommandFailed(mDevice);
+ }
+
+ private List<AudioInputControl> prepareValidAmbientControls() {
+ List<AudioInputControl> controls = new ArrayList<>();
+ final int controlsCount = 2;
+ for (int i = 0; i < controlsCount; i++) {
+ controls.add(prepareAudioInputControl(
+ AudioInputControl.AUDIO_INPUT_TYPE_AMBIENT,
+ AudioInputControl.GAIN_MODE_MANUAL,
+ AudioInputControl.AUDIO_INPUT_STATUS_ACTIVE));
+ }
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(controls);
+ return controls;
+ }
+
+ private List<AudioInputControl> prepareStreamingControls() {
+ List<AudioInputControl> controls = new ArrayList<>();
+ final int controlsCount = 2;
+ for (int i = 0; i < controlsCount; i++) {
+ controls.add(prepareAudioInputControl(
+ AudioInputControl.AUDIO_INPUT_TYPE_STREAMING,
+ AudioInputControl.GAIN_MODE_MANUAL,
+ AudioInputControl.AUDIO_INPUT_STATUS_ACTIVE));
+ }
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(controls);
+ return controls;
+ }
+
+ private List<AudioInputControl> prepareAutomaticAmbientControls() {
+ List<AudioInputControl> controls = new ArrayList<>();
+ final int controlsCount = 2;
+ for (int i = 0; i < controlsCount; i++) {
+ controls.add(prepareAudioInputControl(
+ AudioInputControl.AUDIO_INPUT_TYPE_STREAMING,
+ AudioInputControl.GAIN_MODE_AUTOMATIC,
+ AudioInputControl.AUDIO_INPUT_STATUS_ACTIVE));
+ }
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(controls);
+ return controls;
+ }
+
+ private List<AudioInputControl> prepareInactiveAmbientControls() {
+ List<AudioInputControl> controls = new ArrayList<>();
+ final int controlsCount = 2;
+ for (int i = 0; i < controlsCount; i++) {
+ controls.add(prepareAudioInputControl(
+ AudioInputControl.AUDIO_INPUT_TYPE_STREAMING,
+ AudioInputControl.GAIN_MODE_AUTOMATIC,
+ AudioInputControl.AUDIO_INPUT_STATUS_INACTIVE));
+ }
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(controls);
+ return controls;
+ }
+
+ private AudioInputControl prepareAudioInputControl(int type, int mode, int status) {
+ AudioInputControl control = mock(AudioInputControl.class);
+ when(control.getAudioInputType()).thenReturn(type);
+ when(control.getGainMode()).thenReturn(mode);
+ when(control.getAudioInputStatus()).thenReturn(status);
+ return control;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 8cc997414d70..05f471f62f1d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -139,6 +139,11 @@ public class CachedBluetoothDeviceManagerTest {
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
+
+ mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
+ mCachedDeviceManager.mCachedDevices));
+ mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
+ doNothing().when(mHearingAidDeviceManager).clearLocalDataIfNeeded(any());
}
/**
@@ -338,6 +343,8 @@ public class CachedBluetoothDeviceManagerTest {
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
+
+ verify(mHearingAidDeviceManager).clearLocalDataIfNeeded(cachedDevice2);
verify(mDevice1).removeBond();
}
@@ -353,6 +360,8 @@ public class CachedBluetoothDeviceManagerTest {
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+
+ verify(mHearingAidDeviceManager).clearLocalDataIfNeeded(cachedDevice1);
verify(mDevice2).removeBond();
}
@@ -406,9 +415,6 @@ public class CachedBluetoothDeviceManagerTest {
*/
@Test
public void updateHearingAidDevices_directToHearingAidDeviceManager() {
- mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
- mCachedDeviceManager.mCachedDevices));
- mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
mCachedDeviceManager.updateHearingAidsDevices();
verify(mHearingAidDeviceManager).updateHearingAidsDevices();
@@ -535,6 +541,7 @@ public class CachedBluetoothDeviceManagerTest {
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+ verify(mHearingAidDeviceManager).clearLocalDataIfNeeded(cachedDevice1);
verify(mDevice2).removeBond();
assertThat(cachedDevice1.getGroupId()).isEqualTo(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
@@ -559,6 +566,7 @@ public class CachedBluetoothDeviceManagerTest {
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
+ verify(mHearingAidDeviceManager).clearLocalDataIfNeeded(cachedDevice2);
verify(mDevice1).removeBond();
assertThat(cachedDevice2.getGroupId()).isEqualTo(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
@@ -611,10 +619,7 @@ public class CachedBluetoothDeviceManagerTest {
@Test
public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() {
- mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
- mCachedDeviceManager.mCachedDevices));
doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any());
- mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
cachedDevice1.setHearingAidInfo(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index bf927a1eb4cc..eb73eee90f0d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -17,7 +17,6 @@ package com.android.settingslib.bluetooth;
import static android.bluetooth.BluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.bluetooth.BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT;
-import static android.bluetooth.BluetoothLeAudio.AUDIO_LOCATION_INVALID;
import static com.android.settingslib.bluetooth.HapClientProfile.HearingAidType.TYPE_BINAURAL;
import static com.android.settingslib.bluetooth.HapClientProfile.HearingAidType.TYPE_INVALID;
@@ -272,14 +271,14 @@ public class HearingAidDeviceManagerTest {
*
* Conditions:
* 1) LeAudio hearing aid
- * 2) Invalid audio location and device type
+ * 2) Invalid device type
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_leAudio_invalidInfo_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
- when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_INVALID);
+ when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_FRONT_LEFT);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_INVALID);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
@@ -506,14 +505,14 @@ public class HearingAidDeviceManagerTest {
*
* Conditions:
* 1) LeAudio hearing aid
- * 2) Invalid audio location and device type
+ * 2) Invalid device type
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void updateHearingAidsDevices_leAudio_invalidInfo_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
- when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_INVALID);
+ when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_FRONT_LEFT);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_INVALID);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
index b659c02a2540..6d83588e0f6e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
@@ -194,6 +194,19 @@ public class HearingDeviceLocalDataManagerTest {
verify(mListener).onDeviceLocalDataChange(TEST_ADDRESS, newData);
}
+ @Test
+ public void clear_dataIsRemoved() {
+ String settings = Settings.Global.getStringForUser(mContext.getContentResolver(),
+ Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, UserHandle.USER_SYSTEM);
+ assertThat(settings.contains(TEST_ADDRESS)).isTrue();
+
+ HearingDeviceLocalDataManager.clear(mContext, mDevice);
+
+ settings = Settings.Global.getStringForUser(mContext.getContentResolver(),
+ Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, UserHandle.USER_SYSTEM);
+ assertThat(settings.contains(TEST_ADDRESS)).isFalse();
+ }
+
private void prepareTestDataInSettings() {
String data = generateSettingsString(TEST_ADDRESS, TEST_AMBIENT, TEST_GROUP_AMBIENT,
TEST_AMBIENT_CONTROL_EXPANDED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index d808a25ebc04..9c34946f0c1b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -361,6 +361,26 @@ public class InputRouteManagerTest {
}
@Test
+ public void onAudioDevicesAdded_doNotActivatePreexistingDevice() {
+ final AudioManager audioManager = mock(AudioManager.class);
+ InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+
+ final AudioDeviceInfo info = mockWiredHeadsetInfo();
+ InputMediaDevice device = createInputMediaDeviceFromDeviceInfo(info);
+ inputRouteManager.mInputMediaDevices.add(device);
+
+ // Trigger onAudioDevicesAdded with a device that already exists in the device list.
+ AudioDeviceInfo[] devices = {info};
+ inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+ // The device should not be activated.
+ for (@MediaRecorder.Source int preset : PRESETS) {
+ verify(audioManager, never())
+ .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+ }
+ }
+
+ @Test
public void onAudioDevicesRemoved_shouldApplyDefaultSelectedDeviceToAllPresets() {
final AudioManager audioManager = mock(AudioManager.class);
InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1f291cdefb03..18bebd40b03a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -52,6 +52,7 @@ public class SecureSettings {
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
Settings.Secure.CONTRAST_LEVEL,
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
@@ -99,6 +100,7 @@ public class SecureSettings {
Settings.Secure.RTT_CALLING_MODE,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
+ Settings.Secure.MIRROR_BUILT_IN_DISPLAY,
Settings.Secure.MATCH_CONTENT_FRAME_RATE,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index abd5b9a4a4bb..1d7608d7d4d0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -88,6 +88,9 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ new DiscreteValueValidator(new String[] {"0", "1", "2"}));
VALIDATORS.put(Secure.CONTRAST_LEVEL, new InclusiveFloatRangeValidator(-1f, 1f));
VALIDATORS.put(
Secure.ACCESSIBILITY_CAPTIONING_PRESET,
@@ -150,6 +153,7 @@ public class SecureSettingsValidators {
Secure.INCALL_POWER_BUTTON_BEHAVIOR,
new DiscreteValueValidator(new String[] {"1", "2"}));
VALIDATORS.put(Secure.MINIMAL_POST_PROCESSING_ALLOWED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.MIRROR_BUILT_IN_DISPLAY, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.MATCH_CONTENT_FRAME_RATE,
new DiscreteValueValidator(new String[] {"0", "1", "2"}));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index aca2c4ef2a49..91ac34ac8233 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -72,7 +72,6 @@ import java.util.regex.Pattern;
public final class DeviceConfigService extends Binder {
private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
"/system/etc/aconfig_flags.pb",
- "/system_ext/etc/aconfig_flags.pb",
"/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
@@ -133,12 +132,7 @@ public final class DeviceConfigService extends Binder {
}
pw.println("DeviceConfig provider: ");
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd)) {
- DeviceConfig.dump(pfd, pw, /* prefix= */ " ", args);
- } catch (IOException e) {
- pw.print("IOException creating ParcelFileDescriptor: ");
- pw.println(e);
- }
+ DeviceConfig.dump(pw, /* prefix= */ " ", args);
}
IContentProvider iprovider = mProvider.getIContentProvider();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
index 7eff16b0def4..0367fe0dab01 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
+++ b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.providers.settings;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9ab853ff4964..1c4def39eaa0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt;
import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.FullBackupDataOutput;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -34,6 +35,7 @@ import android.net.Uri;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
+import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
@@ -82,6 +84,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.HashMap;
import java.util.zip.CRC32;
/**
@@ -194,6 +197,22 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY =
"pin_enhanced_privacy";
+ // Error messages for logging metrics.
+ private static final String ERROR_COULD_NOT_READ_FROM_CURSOR =
+ "could_not_read_from_cursor";
+ private static final String ERROR_FAILED_TO_WRITE_ENTITY =
+ "failed_to_write_entity";
+ private static final String ERROR_COULD_NOT_READ_ENTITY =
+ "could_not_read_entity";
+ private static final String ERROR_SKIPPED_BY_SYSTEM = "skipped_by_system";
+ private static final String ERROR_SKIPPED_BY_BLOCKLIST =
+ "skipped_by_dynamic_blocklist";
+ private static final String ERROR_SKIPPED_PRESERVED = "skipped_preserved";
+ private static final String ERROR_SKIPPED_DUE_TO_LARGE_SCREEN =
+ "skipped_due_to_large_screen";
+ private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+
+
// Name of the temporary file we use during full backup/restore. This is
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
@@ -224,6 +243,10 @@ public class SettingsBackupAgent extends BackupAgentHelper {
// The font_scale default value for this device.
private float mDefaultFontScale;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
+ @VisibleForTesting boolean areAgentMetricsEnabled = false;
+ @VisibleForTesting protected Map<String, Integer> numberOfSettingsPerKey;
+
@Override
public void onCreate() {
if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
@@ -232,6 +255,11 @@ public class SettingsBackupAgent extends BackupAgentHelper {
.getStringArray(R.array.entryvalues_font_size);
mSettingsHelper = new SettingsHelper(this);
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
+ mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ numberOfSettingsPerKey = new HashMap<>();
+ areAgentMetricsEnabled = true;
+ }
super.onCreate();
}
@@ -242,7 +270,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
byte[] secureSettingsData = getSecureSettings();
byte[] globalSettingsData = getGlobalSettings();
byte[] lockSettingsData = getLockSettings(UserHandle.myUserId());
- byte[] locale = mSettingsHelper.getLocaleData();
+ byte[] locale = getLocaleSettings();
byte[] softApConfigData = getSoftAPConfiguration();
byte[] netPoliciesData = getNetworkPolicies();
byte[] wifiFullConfigData = getNewWifiConfigData();
@@ -356,7 +384,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, /* movedToSystem= */ null,
R.array.restore_blocked_system_settings, dynamicBlockList,
- preservedSystemSettings);
+ preservedSystemSettings, KEY_SYSTEM);
mSettingsHelper.applyAudioSettings();
break;
@@ -364,13 +392,13 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal,
/* movedToSecure= */ null, movedToSystem,
R.array.restore_blocked_secure_settings, dynamicBlockList,
- preservedSecureSettings);
+ preservedSecureSettings, KEY_SECURE);
break;
case KEY_GLOBAL :
restoreSettings(data, Settings.Global.CONTENT_URI, /* movedToGlobal= */ null,
movedToSecure, movedToSystem, R.array.restore_blocked_global_settings,
- dynamicBlockList, preservedGlobalSettings);
+ dynamicBlockList, preservedGlobalSettings, KEY_GLOBAL);
break;
case KEY_WIFI_SUPPLICANT :
@@ -381,7 +409,12 @@ public class SettingsBackupAgent extends BackupAgentHelper {
case KEY_LOCALE :
byte[] localeData = new byte[size];
data.readEntityData(localeData, 0, size);
- mSettingsHelper.setLocaleData(localeData, size);
+ mSettingsHelper
+ .setLocaleData(
+ localeData,
+ size,
+ mBackupRestoreEventLogger,
+ KEY_LOCALE);
break;
case KEY_WIFI_CONFIG :
@@ -489,7 +522,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, /* movedToSystem= */ null,
R.array.restore_blocked_system_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_SYSTEM);
// secure settings
nBytes = in.readInt();
@@ -499,7 +532,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal,
/* movedToSecure= */ null, movedToSystem,
R.array.restore_blocked_secure_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_SECURE);
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -510,7 +543,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI,
/* movedToGlobal= */ null, movedToSecure, movedToSystem,
R.array.restore_blocked_global_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_GLOBAL);
}
// locale
@@ -518,7 +551,9 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- mSettingsHelper.setLocaleData(buffer, nBytes);
+ mSettingsHelper
+ .setLocaleData(
+ buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
// Restore older backups performing the necessary migrations.
if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
@@ -654,23 +689,41 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (oldChecksum == newChecksum) {
return oldChecksum;
}
+ writeDataForKey(key, data, output);
+ return newChecksum;
+ }
+
+ @VisibleForTesting
+ void writeDataForKey(String key, byte[] data, BackupDataOutput output) {
+ boolean shouldLogMetrics =
+ areAgentMetricsEnabled && numberOfSettingsPerKey.containsKey(key);
try {
if (DEBUG_BACKUP) {
Log.v(TAG, "Writing entity " + key + " of size " + data.length);
}
output.writeEntityHeader(key, data.length);
output.writeEntityData(data, data.length);
+ if (shouldLogMetrics) {
+ mBackupRestoreEventLogger
+ .logItemsBackedUp(key, numberOfSettingsPerKey.get(key));
+ }
} catch (IOException ioe) {
// Bail
+ if (shouldLogMetrics) {
+ mBackupRestoreEventLogger
+ .logItemsBackupFailed(
+ key,
+ numberOfSettingsPerKey.get(key),
+ ERROR_FAILED_TO_WRITE_ENTITY);
+ }
}
- return newChecksum;
}
private byte[] getSystemSettings() {
Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP);
+ return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP, KEY_SYSTEM);
} finally {
cursor.close();
}
@@ -680,7 +733,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP);
+ return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP, KEY_SECURE);
} finally {
cursor.close();
}
@@ -690,7 +743,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, getGlobalSettingsToBackup());
+ return extractRelevantValues(cursor, getGlobalSettingsToBackup(), KEY_GLOBAL);
} finally {
cursor.close();
}
@@ -773,7 +826,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
return baos.toByteArray();
}
- private void restoreSettings(
+ @VisibleForTesting
+ void restoreSettings(
BackupDataInput data,
Uri contentUri,
Set<String> movedToGlobal,
@@ -781,12 +835,17 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
} catch (IOException ioe) {
Log.e(TAG, "Couldn't read entity data");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_COULD_NOT_READ_ENTITY);
+ }
return;
}
restoreSettings(
@@ -798,7 +857,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
- settingsToPreserve);
+ settingsToPreserve,
+ settingsKey);
}
private void restoreSettings(
@@ -810,7 +870,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
restoreSettings(
settings,
0,
@@ -821,7 +882,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
- settingsToPreserve);
+ settingsToPreserve,
+ settingsKey);
}
@VisibleForTesting
@@ -835,12 +897,13 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
- SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
+ SettingsBackupAllowlist allowlist = getBackupAllowlist(contentUri);
// Restore only the white list data.
final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
@@ -850,7 +913,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
- for (String key : whitelist.mSettingsWhitelist) {
+ int restoredSettingsCount = 0;
+ for (String key : allowlist.mSettingsAllowlist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
Log.i(
@@ -860,6 +924,12 @@ public class SettingsBackupAgent extends BackupAgentHelper {
+ " removed from restore by "
+ (isBlockedBySystem ? "system" : "dynamic")
+ " block list");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey,
+ /* count= */ 1,
+ isBlockedBySystem ? ERROR_SKIPPED_BY_SYSTEM : ERROR_SKIPPED_BY_BLOCKLIST);
+ }
continue;
}
@@ -870,12 +940,20 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) {
Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+ "preserved");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_SKIPPED_PRESERVED);
+ }
continue;
}
if (LargeScreenSettings.doNotRestoreIfLargeScreenSetting(key, getBaseContext())) {
Log.i(TAG, "Skipping restore for setting " + key + " as the target device "
+ "is a large screen (i.e tablet or foldable in unfolded state)");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_SKIPPED_DUE_TO_LARGE_SCREEN);
+ }
continue;
}
@@ -912,19 +990,34 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
// only restore the settings that have valid values
- if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
+ if (!isValidSettingValue(key, value, allowlist.mSettingsValidators)) {
Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
+ " validation, value: " + value);
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_DID_NOT_PASS_VALIDATION);
+ }
continue;
}
final Uri destination;
+ // If the destination changes, we need to update the key used as datatype for metrics.
+ String finalSettingsKey = settingsKey;
if (movedToGlobal != null && movedToGlobal.contains(key)) {
destination = Settings.Global.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_GLOBAL;
+ }
} else if (movedToSecure != null && movedToSecure.contains(key)) {
destination = Settings.Secure.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_SECURE;
+ }
} else if (movedToSystem != null && movedToSystem.contains(key)) {
destination = Settings.System.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_SYSTEM;
+ }
} else {
destination = contentUri;
}
@@ -942,6 +1035,10 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (isSettingPreserved) {
Log.i(TAG, "Skipping restore for setting navigation_mode "
+ "as it is marked as preserved");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ finalSettingsKey, /* count= */ 1, ERROR_SKIPPED_PRESERVED);
+ }
continue;
}
}
@@ -961,12 +1058,16 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
}
-
+ // TODO(b/379861078): Log metrics inside this method.
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestored(finalSettingsKey, /* count= */ 1);
+ }
}
+
}
@@ -996,29 +1097,29 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
@VisibleForTesting
- SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ SettingsBackupAllowlist getBackupAllowlist(Uri contentUri) {
// Figure out the white list and redirects to the global table. We restore anything
// in either the backup allowlist or the legacy-restore allowlist for this table.
- String[] whitelist;
+ String[] allowlist;
Map<String, Validator> validators = null;
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
+ allowlist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
Settings.Secure.LEGACY_RESTORE_SETTINGS,
DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
validators = SecureSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.System.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
+ allowlist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
Settings.System.LEGACY_RESTORE_SETTINGS);
validators = SystemSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
+ allowlist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
Settings.Global.LEGACY_RESTORE_SETTINGS);
validators = GlobalSettingsValidators.VALIDATORS;
} else {
throw new IllegalArgumentException("Unknown URI: " + contentUri);
}
- return new SettingsBackupWhitelist(whitelist, validators);
+ return new SettingsBackupAllowlist(allowlist, validators);
}
private String[] getGlobalSettingsToBackup() {
@@ -1118,11 +1219,20 @@ public class SettingsBackupAgent extends BackupAgentHelper {
*
* @param cursor A cursor with settings data.
* @param settings The settings to extract.
+ * @param settingsKey The key of the settings to extract (eg system).
* @return The byte array of extracted values.
*/
- private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
+ private byte[] extractRelevantValues(
+ Cursor cursor, String[] settings, String settingsKey) {
if (!cursor.moveToFirst()) {
Log.e(TAG, "Couldn't read from the cursor");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger
+ .logItemsBackupFailed(
+ settingsKey,
+ settings.length,
+ ERROR_COULD_NOT_READ_FROM_CURSOR);
+ }
return new byte[0];
}
@@ -1181,6 +1291,10 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
}
+ if (areAgentMetricsEnabled) {
+ numberOfSettingsPerKey.put(settingsKey, backedUpSettingIndex);
+ }
+
// Aggregate the result.
byte[] result = new byte[totalSize];
int pos = 0;
@@ -1304,6 +1418,15 @@ public class SettingsBackupAgent extends BackupAgentHelper {
return mWifiManager.retrieveBackupData();
}
+ private byte[] getLocaleSettings() {
+ if (!areAgentMetricsEnabled) {
+ return mSettingsHelper.getLocaleData();
+ }
+ LocaleList localeList = mSettingsHelper.getLocaleList();
+ numberOfSettingsPerKey.put(KEY_LOCALE, localeList.size());
+ return localeList.toLanguageTags().getBytes();
+ }
+
private void restoreNewWifiConfigData(byte[] bytes) {
if (DEBUG_BACKUP) {
Log.v(TAG, "Applying restored wifi data");
@@ -1364,7 +1487,9 @@ public class SettingsBackupAgent extends BackupAgentHelper {
getContentResolver()
.query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null)) {
return extractRelevantValues(
- cursor, DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ cursor,
+ DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP,
+ KEY_DEVICE_SPECIFIC_CONFIG);
}
}
@@ -1399,7 +1524,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
null,
blockedSettingsArrayId,
dynamicBlocklist,
- preservedSettings);
+ preservedSettings,
+ KEY_DEVICE_SPECIFIC_CONFIG);
updateWindowManagerIfNeeded(originalDensity);
@@ -1597,14 +1723,14 @@ public class SettingsBackupAgent extends BackupAgentHelper {
* Store the allowlist of settings to be backed up and validators for them.
*/
@VisibleForTesting
- static class SettingsBackupWhitelist {
- final String[] mSettingsWhitelist;
+ static class SettingsBackupAllowlist {
+ final String[] mSettingsAllowlist;
final Map<String, Validator> mSettingsValidators;
- SettingsBackupWhitelist(String[] settingsWhitelist,
+ SettingsBackupAllowlist(String[] settingsAllowlist,
Map<String, Validator> settingsValidators) {
- mSettingsWhitelist = settingsWhitelist;
+ mSettingsAllowlist = settingsAllowlist;
mSettingsValidators = settingsValidators;
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index ea8ae7b208cd..9bbb509f323a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -19,11 +19,14 @@ package com.android.providers.settings;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.hardware.display.ColorDisplayManager;
import android.icu.util.ULocale;
@@ -31,6 +34,7 @@ import android.media.AudioManager;
import android.media.RingtoneManager;
import android.media.Utils;
import android.net.Uri;
+import android.os.Build;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -43,11 +47,13 @@ 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.List;
import java.util.Locale;
import java.util.Set;
@@ -67,6 +73,13 @@ public class SettingsHelper {
private static final int LONG_PRESS_POWER_FOR_ASSISTANT = 5;
/** See frameworks/base/core/res/res/values/config.xml#config_keyChordPowerVolumeUp **/
private static final int KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS = 2;
+ @VisibleForTesting
+ static final String HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION =
+ "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED";
+
+ // Error messages for logging metrics.
+ private static final String ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA =
+ "remote_exception_setting_locale_data";
private Context mContext;
private AudioManager mAudioManager;
@@ -246,6 +259,23 @@ public class SettingsHelper {
// Don't write it to setting. Let the broadcast receiver in
// AccessibilityManagerService handle restore/merging logic.
return;
+ } else if (com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect()
+ && Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED.equals(name)) {
+ final boolean currentlyEnabled = Settings.Secure.getInt(
+ context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1;
+ final boolean enabledInRestore = value != null && Integer.parseInt(value) == 1;
+
+ // If restoring from Android 15 or earlier and the user didn't already enable HCT
+ // on this new device, then don't restore and trigger custom migration logic.
+ final boolean needsCustomMigration = !currentlyEnabled
+ && restoredFromSdkInt < Build.VERSION_CODES.BAKLAVA
+ && enabledInRestore;
+ if (needsCustomMigration) {
+ migrateHighContrastText(context);
+ return;
+ }
+ // fall through to the ordinary write to settings
}
// Default case: write the restored value to settings
@@ -523,11 +553,40 @@ public class SettingsHelper {
}
}
+ private static void migrateHighContrastText(Context context) {
+ final Intent intent = new Intent(HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION)
+ .setPackage(getSettingsAppPackage(context));
+ context.sendBroadcastAsUser(intent, context.getUser(), null);
+ }
+
+ /**
+ * Returns the System Settings application's package name
+ */
+ private static String getSettingsAppPackage(Context context) {
+ String settingsAppPackage = null;
+ PackageManager packageManager = context.getPackageManager();
+ if (packageManager != null) {
+ List<ResolveInfo> results = packageManager.queryIntentActivities(
+ new Intent(Settings.ACTION_SETTINGS),
+ PackageManager.MATCH_SYSTEM_ONLY);
+ if (!results.isEmpty()) {
+ settingsAppPackage = results.getFirst().activityInfo.applicationInfo.packageName;
+ }
+ }
+
+ return !TextUtils.isEmpty(settingsAppPackage) ? settingsAppPackage : "com.android.settings";
+ }
+
/* package */ byte[] getLocaleData() {
Configuration conf = mContext.getResources().getConfiguration();
return conf.getLocales().toLanguageTags().getBytes();
}
+ LocaleList getLocaleList() {
+ Configuration conf = mContext.getResources().getConfiguration();
+ return conf.getLocales();
+ }
+
private static Locale toFullLocale(@NonNull Locale locale) {
if (locale.getScript().isEmpty() || locale.getCountry().isEmpty()) {
return ULocale.addLikelySubtags(ULocale.forLocale(locale)).toLocale();
@@ -653,8 +712,12 @@ public class SettingsHelper {
* code and {@code CC} is a two letter country code.
*
* @param data the comma separated BCP-47 language tags in bytes.
+ * @param size the size of the data in bytes.
+ * @param backupRestoreEventLogger the logger to log the restore event.
+ * @param dataType the data type of the setting for logging purposes.
*/
- /* package */ void setLocaleData(byte[] data, int size) {
+ /* package */ void setLocaleData(
+ byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
final Configuration conf = mContext.getResources().getConfiguration();
// Replace "_" with "-" to deal with older backups.
@@ -681,8 +744,18 @@ public class SettingsHelper {
am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
mContext.getAttributionTag());
+ if (Flags.enableMetricsSettingsBackupAgents()) {
+ backupRestoreEventLogger
+ .logItemsRestored(dataType, localeList.size());
+ }
} catch (RemoteException e) {
- // Intentionally left blank
+ if (Flags.enableMetricsSettingsBackupAgents()) {
+ backupRestoreEventLogger
+ .logItemsRestoreFailed(
+ dataType,
+ localeList.size(),
+ ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
+ }
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 37eda3ebf9a8..f9c64422b0db 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1778,6 +1778,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ SecureSettingsProto.Accessibility.HCT_RECT_PROMPT_STATUS);
+ dumpSetting(s, p,
Settings.Secure.CONTRAST_LEVEL,
SecureSettingsProto.Accessibility.CONTRAST_LEVEL);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6128d45831fb..55f48e3e367f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2089,7 +2089,33 @@ public class SettingsProvider extends ContentProvider {
// setting.
return false;
}
- final String mimeType = getContext().getContentResolver().getType(audioUri);
+
+ // If the audioUri comes from FileProvider, the security check will fail. Currently, it
+ // should not have too many FileProvider Uri usage, using a workaround fix here.
+ // Only allow for caller is privileged apps
+ ApplicationInfo aInfo = null;
+ try {
+ aInfo = getCallingApplicationInfoOrThrow();
+ } catch (IllegalStateException ignored) {
+ Slog.w(LOG_TAG, "isValidMediaUri: cannot get calling app info for setting: "
+ + name + " URI: " + audioUri);
+ return false;
+ }
+ final boolean isPrivilegedApp = aInfo != null ? aInfo.isPrivilegedApp() : false;
+ String mimeType = null;
+ if (isPrivilegedApp) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mimeType = getContext().getContentResolver().getType(audioUri);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ mimeType = getContext().getContentResolver().getType(audioUri);
+ }
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "isValidMediaUri mimeType: " + mimeType);
+ }
if (mimeType == null) {
Slog.e(LOG_TAG,
"mutateSystemSetting for setting: " + name + " URI: " + audioUri
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c0e61eefb4dd..5cd534e62ea9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -171,7 +171,6 @@ final class SettingsState {
private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
"/system/etc/aconfig_flags.pb",
- "/system_ext/etc/aconfig_flags.pb",
"/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
@@ -409,6 +408,11 @@ final class SettingsState {
Slog.w(LOG_TAG, "Bulk sync request to acongid failed.");
}
}
+
+ if (Flags.disableBulkCompare()) {
+ return;
+ }
+
// TOBO(b/312444587): remove the comparison logic after Test Mission 2.
if (requests == null) {
Map<String, AconfigdFlagInfo> aconfigdFlagMap =
@@ -421,7 +425,7 @@ final class SettingsState {
}
}
- // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+ // TODO(b/312444587): remove the comparison logic after Test Mission 2.
public int compareFlagValueInNewStorage(
Map<String, AconfigdFlagInfo> defaultFlagMap,
Map<String, AconfigdFlagInfo> aconfigdFlagMap) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index aca26ecce29a..cfd27c69032e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -101,3 +101,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "disable_bulk_compare"
+ namespace: "core_experiments_team_internal"
+ description: "Disable bulk comparison between DeviceConfig and aconfig storage."
+ bug: "312444587"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9004488c2e12..c88a7fd834d6 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -182,6 +182,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
+ Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS,
Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 3a39150523ac..350c149f40de 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -17,11 +17,23 @@
package com.android.providers.settings;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
-
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupRestoreEventLogger;
+import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -32,7 +44,10 @@ import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
+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.provider.settings.validators.SettingsValidators;
import android.provider.settings.validators.Validator;
@@ -43,9 +58,14 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.window.flags.Flags;
+import java.util.List;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -54,12 +74,14 @@ 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.atomic.AtomicInteger;
import java.util.function.Function;
+
/**
* Tests for the SettingsHelperTest
* Usage: atest SettingsProviderTest:SettingsBackupAgentTest
@@ -73,6 +95,17 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>();
private static final Map<String, String> TEST_VALUES = new HashMap<>();
private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>();
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_VALUE = "test_value";
+ private static final String ERROR_COULD_NOT_READ_ENTITY = "could_not_read_entity";
+ private static final String ERROR_SKIPPED_BY_SYSTEM = "skipped_by_system";
+ private static final String ERROR_SKIPPED_BY_BLOCKLIST =
+ "skipped_by_dynamic_blocklist";
+ private static final String ERROR_SKIPPED_PRESERVED = "skipped_preserved";
+ private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+ private static final String KEY_SYSTEM = "system";
+ private static final String KEY_SECURE = "secure";
+ private static final String KEY_GLOBAL = "global";
static {
DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED,
@@ -86,6 +119,14 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR);
}
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private BackupDataInput mBackupDataInput;
+ @Mock private BackupDataOutput mBackupDataOutput;
+
private TestFriendlySettingsBackupAgent mAgentUnderTest;
private Context mContext;
@@ -203,19 +244,32 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
@Test
public void testOnRestore_preservedSettingsAreNotRestored() {
- SettingsBackupAgent.SettingsBackupWhitelist whitelist =
- new SettingsBackupAgent.SettingsBackupWhitelist(
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
new String[] { OVERRIDDEN_TEST_SETTING, PRESERVED_TEST_SETTING },
TEST_VALUES_VALIDATORS);
- mAgentUnderTest.setSettingsWhitelist(whitelist);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
mAgentUnderTest.setBlockedSettings();
TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
mAgentUnderTest.mSettingsHelper = settingsHelper;
byte[] backupData = generateBackupData(TEST_VALUES);
- mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI,
- null, null, null, /* blockedSettingsArrayId */ 0, Collections.emptySet(),
- new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
+ mAgentUnderTest.restoreSettings(
+ backupData,
+ /* pos */ 0,
+ backupData.length,
+ TEST_URI,
+ null,
+ null,
+ null,
+ /* blockedSettingsArrayId */ 0,
+ Collections.emptySet(),
+ new HashSet<>(Collections
+ .singletonList(
+ SettingsBackupAgent
+ .getQualifiedKeyForSetting(
+ PRESERVED_TEST_SETTING, TEST_URI))),
+ TEST_KEY);
assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
@@ -262,6 +316,486 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
assertEquals("1.5", testedMethod.apply("1.8"));
}
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void onCreate_metricsFlagIsDisabled_areAgentMetricsEnabledIsFalse() {
+ mAgentUnderTest.onCreate();
+
+ assertFalse(mAgentUnderTest.areAgentMetricsEnabled);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void onCreate_flagIsEnabled_areAgentMetricsEnabledIsTrue() {
+ mAgentUnderTest.onCreate();
+
+ assertTrue(mAgentUnderTest.areAgentMetricsEnabled);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_dataWriteSucceeds_logsSuccessMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityHeaderFails_logsFailureMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenThrow(new IOException());
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityDataFails_logsFailureMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsDisabled_doesNotLogMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyDoesNotContainKey_doesNotLogMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_agentMetricsAreLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreDisabled_agentMetricsAreNotLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNull(loggingResult);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_readEntityDataFails_failureIsLogged()
+ throws IOException {
+ when(mBackupDataInput.readEntityData(any(byte[].class), anyInt(), anyInt()))
+ .thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+
+ mAgentUnderTest.restoreSettings(
+ mBackupDataInput,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_COULD_NOT_READ_ENTITY));
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreDisabled_readEntityDataFails_failureIsNotLogged()
+ throws IOException {
+ when(mBackupDataInput.readEntityData(any(byte[].class), anyInt(), anyInt()))
+ .thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+
+ mAgentUnderTest.restoreSettings(
+ mBackupDataInput,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsSkippedBySystem_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ String[] settingBlockedBySystem = new String[] {OVERRIDDEN_TEST_SETTING};
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ settingBlockedBySystem,
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings(settingBlockedBySystem);
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_BY_SYSTEM));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsSkippedByBlockList_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+ Set<String> dynamicBlockList =
+ Set.of(Uri.withAppendedPath(TEST_URI, OVERRIDDEN_TEST_SETTING).toString());
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ dynamicBlockList,
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_BY_BLOCKLIST));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsPreserved_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+ Set<String> preservedSettings =
+ Set.of(Uri.withAppendedPath(TEST_URI, OVERRIDDEN_TEST_SETTING).toString());
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList = */ Collections.emptySet(),
+ preservedSettings,
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_PRESERVED));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsNotValid_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ /* settingsValidators= */ null);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList = */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_DID_NOT_PASS_VALIDATION));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToGlobal_agentMetricsAreLoggedWithGlobalKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_GLOBAL, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToSecure_agentMetricsAreLoggedWithSecureKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SECURE, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToSystem_agentMetricsAreLoggedWithSystemKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SYSTEM, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
private byte[] generateBackupData(Map<String, String> keyValueData) {
int totalBytes = 0;
for (String key : keyValueData.keySet()) {
@@ -293,7 +827,8 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
null,
R.array.restore_blocked_global_settings,
/* dynamicBlockList= */ Collections.emptySet(),
- /* settingsToPreserve= */ Collections.emptySet());
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
}
private byte[] generateUncorruptedHeader() throws IOException {
@@ -329,6 +864,21 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
}
}
+ private DataTypeResult getLoggingResultForDatatype(
+ String dataType, SettingsBackupAgent agent) {
+ if (agent.getBackupRestoreEventLogger() == null) {
+ return null;
+ }
+ List<DataTypeResult> loggingResults =
+ agent.getBackupRestoreEventLogger().getLoggingResults();
+ for (DataTypeResult result : loggingResults) {
+ if (result.getDataType().equals(dataType)) {
+ return result;
+ }
+ }
+ return null;
+ }
+
private byte[] generateSingleKeyTestBackupData(String key, String value) throws IOException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
os.write(SettingsBackupAgent.toByteArray(key));
@@ -340,7 +890,7 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
private Boolean mForcedDeviceInfoRestoreAcceptability = null;
private String[] mBlockedSettings = null;
- private SettingsBackupWhitelist mSettingsWhitelist = null;
+ private SettingsBackupAllowlist mSettingsAllowlist = null;
void setForcedDeviceInfoRestoreAcceptability(boolean value) {
mForcedDeviceInfoRestoreAcceptability = value;
@@ -350,8 +900,8 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
mBlockedSettings = blockedSettings;
}
- void setSettingsWhitelist(SettingsBackupWhitelist settingsWhitelist) {
- mSettingsWhitelist = settingsWhitelist;
+ void setSettingsAllowlist(SettingsBackupAllowlist settingsAllowlist) {
+ mSettingsAllowlist = settingsAllowlist;
}
@Override
@@ -369,12 +919,18 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
}
@Override
- SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
- if (mSettingsWhitelist == null) {
- return super.getBackupWhitelist(contentUri);
+ SettingsBackupAllowlist getBackupAllowlist(Uri contentUri) {
+ if (mSettingsAllowlist == null) {
+ return super.getBackupAllowlist(contentUri);
}
- return mSettingsWhitelist;
+ return mSettingsAllowlist;
+ }
+
+ void setNumberOfSettingsPerKey(String key, int numberOfSettings) {
+ if (numberOfSettingsPerKey != null) {
+ this.numberOfSettingsPerKey.put(key, numberOfSettings);
+ }
}
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
index 048d93b09967..8bffba3cb75f 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
@@ -26,15 +26,19 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.SettingsStringUtil;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -48,7 +52,8 @@ import java.util.concurrent.ExecutionException;
*/
@RunWith(AndroidJUnit4.class)
public class SettingsHelperRestoreTest {
-
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final float FLOAT_TOLERANCE = 0.01f;
private Context mContext;
@@ -57,11 +62,99 @@ public class SettingsHelperRestoreTest {
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
mContentResolver = mContext.getContentResolver();
mSettingsHelper = new SettingsHelper(mContext);
}
+ @After
+ public void cleanUp() {
+ setDefaultAccessibilityDisplayMagnificationScale();
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0);
+ Settings.Secure.putString(mContentResolver, Settings.Secure.ACCESSIBILITY_QS_TARGETS, null);
+ Settings.Secure.putString(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null);
+ }
+
+ @Test
+ public void restoreHighTextContrastEnabled_currentlyEnabled_enableInRestoredFromVanilla_dontSendNotification_hctKeepsEnabled()
+ throws ExecutionException, InterruptedException {
+ BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext(
+ mContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ interceptingContext.nextBroadcastIntent(
+ SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+ mContentResolver = interceptingContext.getContentResolver();
+ String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+ Settings.Secure.putInt(mContentResolver, settingName, 1);
+
+ mSettingsHelper.restoreValue(
+ interceptingContext,
+ mContentResolver,
+ new ContentValues(2),
+ Settings.Secure.getUriFor(settingName),
+ settingName,
+ String.valueOf(1),
+ Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+ futureIntent.assertNotReceived();
+ assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1);
+ }
+
+ @EnableFlags(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ @Test
+ public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromVanilla_sendNotification_hctKeepsDisabled()
+ throws ExecutionException, InterruptedException {
+ BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext(
+ mContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ interceptingContext.nextBroadcastIntent(
+ SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+ mContentResolver = interceptingContext.getContentResolver();
+ String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+ Settings.Secure.putInt(mContentResolver, settingName, 0);
+
+ mSettingsHelper.restoreValue(
+ interceptingContext,
+ mContentResolver,
+ new ContentValues(2),
+ Settings.Secure.getUriFor(settingName),
+ settingName,
+ String.valueOf(1),
+ Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+ Intent intentReceived = futureIntent.get();
+ assertThat(intentReceived).isNotNull();
+ assertThat(intentReceived.getPackage()).isNotNull();
+ assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(0);
+ }
+
+ @Test
+ public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromAfterVanilla_dontSendNotification_hctShouldEnabled()
+ throws ExecutionException, InterruptedException {
+ BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext(
+ mContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ interceptingContext.nextBroadcastIntent(
+ SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+ mContentResolver = interceptingContext.getContentResolver();
+ String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+ Settings.Secure.putInt(mContentResolver, settingName, 0);
+
+ mSettingsHelper.restoreValue(
+ interceptingContext,
+ mContentResolver,
+ new ContentValues(2),
+ Settings.Secure.getUriFor(settingName),
+ settingName,
+ String.valueOf(1),
+ Build.VERSION_CODES.BAKLAVA);
+
+ futureIntent.assertNotReceived();
+ assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1);
+ }
+
/** Tests for {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. */
@Test
public void
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 cea2bbc5c535..58200d4f6553 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -33,6 +33,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -83,6 +84,9 @@ public class SettingsHelperTest {
"content://media/internal/audio/media/30?title=DefaultAlarm&canonical=1";
private static final String VIBRATION_FILE_NAME = "haptics.xml";
+ private static final LocaleList LOCALE_LIST =
+ LocaleList.forLanguageTags("en-US,en-UK");
+
private SettingsHelper mSettingsHelper;
@Rule
@@ -778,6 +782,15 @@ public class SettingsHelperTest {
assertThat(getAutoRotationSettingValue()).isEqualTo(previousValue);
}
+ @Test
+ public void getLocaleList_returnsLocaleList() {
+ Configuration config = new Configuration();
+ config.setLocales(LOCALE_LIST);
+ when(mResources.getConfiguration()).thenReturn(config);
+
+ assertThat(mSettingsHelper.getLocaleList()).isEqualTo(LOCALE_LIST);
+ }
+
private int getAutoRotationSettingValue() {
return Settings.System.getInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 48ce49dbfb3a..276b206cd6a1 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -27,6 +27,7 @@ import android.aconfig.Aconfig.parsed_flags;
import android.aconfigd.AconfigdFlagInfo;
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Xml;
@@ -1304,6 +1305,7 @@ public class SettingsStateTest {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_DISABLE_BULK_COMPARE)
public void testCompareFlagValueInNewStorage() {
int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
Object lock = new Object();
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 5f810858b7cd..5fdf0451d2c8 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -12,7 +12,10 @@ shell_srcs = [
"src/**/*.java",
":dumpstate_aidl",
]
-shell_static_libs = ["androidx.legacy_legacy-support-v4"]
+shell_static_libs = [
+ "androidx.legacy_legacy-support-v4",
+ "wear_aconfig_declarations_flags_java_lib",
+]
android_app {
name: "Shell",
@@ -28,6 +31,7 @@ android_app {
flags_packages: [
"android.security.flags-aconfig",
"android.permission.flags-aconfig",
+ "wear_aconfig_declarations",
],
platform_apis: true,
certificate: "platform",
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fa6e2dbe02f3..fb4293a9b5ea 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -348,6 +348,7 @@
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
@@ -989,6 +990,10 @@
<uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE"
android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+ <!-- Permission for TestClassifier tests to get access to classifier by type -->
+ <uses-permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
+ android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index d4b5b86223ea..576afdc824f2 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -13,3 +13,7 @@ cbrubaker@google.com
omakoto@google.com
michaelwr@google.com
ronish@google.com
+
+# Wear Bugreport Owners
+ranamouawi@google.com
+yashasvig@google.com
diff --git a/packages/Shell/res/values/defaults.xml b/packages/Shell/res/values/defaults.xml
new file mode 100644
index 000000000000..b693cc826be0
--- /dev/null
+++ b/packages/Shell/res/values/defaults.xml
@@ -0,0 +1,5 @@
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Default for Wear bugreport warning activity-->
+ <!-- DO NOT TRANSLATE -->
+ <string name="system_ui_wear_bugreport_warning_activity" />
+</resources> \ No newline at end of file
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7f25b51e35ca..c6555041164d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -24,6 +24,7 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.shell.BugreportPrefs.STATE_HIDE;
import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
import static com.android.shell.BugreportPrefs.getWarningState;
+import static com.android.shell.flags.Flags.handleBugreportsForWear;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -89,10 +90,10 @@ import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.google.android.collect.Lists;
-
import libcore.io.Streams;
+import com.google.android.collect.Lists;
+
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -140,6 +141,7 @@ import java.util.zip.ZipOutputStream;
public class BugreportProgressService extends Service {
private static final String TAG = "BugreportProgressService";
private static final boolean DEBUG = false;
+ private static final String WRITE_AND_APPEND_MODE = "wa";
private Intent startSelfIntent;
@@ -255,6 +257,9 @@ public class BugreportProgressService extends Service {
/** Always keep remote bugreport files created in the last day. */
private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
+ /** Minimum delay for sending last update notification */
+ private static final int DELAY_NOTIFICATION_MS = 250;
+
private final Object mLock = new Object();
/** Managed bugreport info (keyed by id) */
@@ -384,7 +389,11 @@ public class BugreportProgressService extends Service {
}
private static String getFileName(BugreportInfo info, String suffix) {
- return String.format("%s-%s%s", info.baseName, info.getName(), suffix);
+ return getFileName(suffix, info.baseName, info.getName());
+ }
+
+ private static String getFileName(String suffix, String baseName, String name) {
+ return String.format("%s-%s%s", baseName, name, suffix);
}
private final class BugreportCallbackImpl extends BugreportCallback {
@@ -420,14 +429,14 @@ public class BugreportProgressService extends Service {
@Override
public void onFinished() {
- mInfo.renameBugreportFile();
- mInfo.renameScreenshots();
- if (mInfo.bugreportFile.length() == 0) {
- Log.e(TAG, "Bugreport file empty. File path = " + mInfo.bugreportFile);
- onError(BUGREPORT_ERROR_RUNTIME);
- return;
- }
synchronized (mLock) {
+ mInfo.renameBugreportFile();
+ mInfo.renameScreenshots();
+ if (mInfo.bugreportLocationInfo.isFileEmpty(mContext)) {
+ Log.e(TAG, "Bugreport file empty. File path = " + mInfo.bugreportLocationInfo);
+ onError(BUGREPORT_ERROR_RUNTIME);
+ return;
+ }
sendBugreportFinishedBroadcastLocked();
mMainThreadHandler.post(() -> mInfoDialog.onBugreportFinished(mInfo));
}
@@ -454,15 +463,15 @@ public class BugreportProgressService extends Service {
@GuardedBy("mLock")
private void sendBugreportFinishedBroadcastLocked() {
- final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
- if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
- sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
- mInfo.bugreportFile, mInfo.nonce);
+ File bugreportFile = mInfo.bugreportLocationInfo.mBugreportFile;
+ if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE && bugreportFile != null) {
+ sendRemoteBugreportFinishedBroadcast(
+ mContext, bugreportFile.getAbsolutePath(), bugreportFile, mInfo.nonce);
} else {
cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
- intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
- intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
+ intent.putExtra(EXTRA_BUGREPORT, mInfo.bugreportLocationInfo.getBugreportPath());
+ intent.putExtra(EXTRA_SCREENSHOT, mInfo.screenshotLocationInfo.getScreenshotPath());
mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
onBugreportFinished(mInfo);
}
@@ -498,19 +507,6 @@ public class BugreportProgressService extends Service {
android.Manifest.permission.DUMP);
}
- /**
- * Checks if screenshot array is non-empty and returns the first screenshot's path. The first
- * screenshot is the default screenshot for the bugreport types that take it.
- */
- private static String getScreenshotForIntent(BugreportInfo info) {
- if (!info.screenshotFiles.isEmpty()) {
- final File screenshotFile = info.screenshotFiles.get(0);
- final String screenshotFilePath = screenshotFile.getAbsolutePath();
- return screenshotFilePath;
- }
- return null;
- }
-
private static String generateFileHash(String fileName) {
String fileHash = null;
try {
@@ -715,24 +711,28 @@ public class BugreportProgressService extends Service {
String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
List<Uri> extraAttachments =
intent.getParcelableArrayListExtra(EXTRA_EXTRA_ATTACHMENT_URIS, Uri.class);
-
- BugreportInfo info = new BugreportInfo(mContext, baseName, name, shareTitle,
- shareDescription, bugreportType, mBugreportsDir, nonce, extraAttachments);
- synchronized (mLock) {
- if (info.bugreportFile.exists()) {
- Log.e(TAG, "Failed to start bugreport generation, the requested bugreport file "
- + info.bugreportFile + " already exists");
- return;
- }
- info.createBugreportFile();
+ BugreportInfo info =
+ setupFilesAndCreateBugreportInfo(
+ intent,
+ bugreportType,
+ baseName,
+ name,
+ shareTitle,
+ shareDescription,
+ nonce,
+ extraAttachments);
+ if (info == null) {
+ Log.e(TAG, "Could not initialize bugreport inputs");
+ return;
}
+
ParcelFileDescriptor bugreportFd = info.getBugreportFd();
if (bugreportFd == null) {
Log.e(TAG, "Failed to start bugreport generation as "
+ " bugreport parcel file descriptor is null.");
return;
}
- info.createScreenshotFile(mBugreportsDir);
+
ParcelFileDescriptor screenshotFd = null;
if (isDefaultScreenshotRequired(bugreportType, /* hasScreenshotButton= */ !mIsTv)) {
screenshotFd = info.getDefaultScreenshotFd();
@@ -740,7 +740,7 @@ public class BugreportProgressService extends Service {
Log.e(TAG, "Failed to start bugreport generation as"
+ " screenshot parcel file descriptor is null. Deleting bugreport file");
FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
+ info.bugreportLocationInfo.maybeDeleteBugreportFile();
return;
}
}
@@ -768,6 +768,56 @@ public class BugreportProgressService extends Service {
}
}
+ // Sets up BugreportInfo. If needed, creates bugreport and screenshot files.
+ private BugreportInfo setupFilesAndCreateBugreportInfo(
+ Intent intent,
+ int bugreportType,
+ String baseName,
+ String name,
+ String shareTitle,
+ String shareDescription,
+ long nonce,
+ List<Uri> extraAttachments) {
+ ArrayList<Uri> brAndScreenshot;
+ Uri bugReportUri = null;
+ Uri screenshotUri = null;
+
+ if (handleBugreportsForWear() && bugreportType == BugreportParams.BUGREPORT_MODE_WEAR) {
+ brAndScreenshot = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ if (brAndScreenshot != null && !brAndScreenshot.isEmpty()) {
+ bugReportUri = brAndScreenshot.get(0);
+ if (bugReportUri == null) {
+ Log.e(TAG, "Can't start bugreport request. Bugreport uri is null.");
+ return null;
+ }
+ screenshotUri = (brAndScreenshot.size() > 1) ? brAndScreenshot.get(1) : null;
+ }
+ }
+
+ BugreportLocationInfo bugreportLocationInfo =
+ new BugreportLocationInfo(bugReportUri, mBugreportsDir, baseName, name);
+ ScreenshotLocationInfo screenshotLocationInfo = new ScreenshotLocationInfo(screenshotUri);
+ BugreportInfo info =
+ new BugreportInfo(
+ mContext,
+ baseName,
+ name,
+ shareTitle,
+ shareDescription,
+ bugreportType,
+ nonce,
+ extraAttachments,
+ bugreportLocationInfo,
+ screenshotLocationInfo);
+ synchronized (mLock) {
+ if (!bugreportLocationInfo.maybeCreateBugreportFile()) {
+ return null;
+ }
+ }
+ info.maybeCreateScreenshotFile(mBugreportsDir);
+ return info;
+ }
+
private static boolean isDefaultScreenshotRequired(
@BugreportParams.BugreportMode int bugreportType,
boolean hasScreenshotButton) {
@@ -880,6 +930,7 @@ public class BugreportProgressService extends Service {
Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
}
info.lastProgress.set(progress);
+ info.lastUpdate.set(System.currentTimeMillis());
sendForegroundabledNotification(info.id, builder.build());
}
@@ -1177,8 +1228,9 @@ public class BugreportProgressService extends Service {
stopForegroundWhenDoneLocked(info.id);
}
- if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
- Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ if (!info.bugreportLocationInfo.isValidBugreportResult()) {
+ Log.e(TAG, "Could not read bugreport file " + bugreportFile);
Toast.makeText(mContext, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
synchronized (mLock) {
stopProgressLocked(info.id);
@@ -1194,7 +1246,7 @@ public class BugreportProgressService extends Service {
* the bugreport.
*/
private void triggerLocalNotification(final BugreportInfo info) {
- boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
+ boolean isPlainText = info.bugreportLocationInfo.isPlainText();
if (!isPlainText) {
// Already zipped, send it right away.
sendBugreportNotification(info, mTakingScreenshot);
@@ -1223,11 +1275,11 @@ public class BugreportProgressService extends Service {
// grant temporary permissions for.
final Uri bugreportUri;
try {
- bugreportUri = getUri(context, info.bugreportFile);
+ bugreportUri = getUri(context, info.bugreportLocationInfo.mBugreportFile);
} catch (IllegalArgumentException e) {
// Should not happen on production, but happens when a Shell is sideloaded and
// FileProvider cannot find a configured root for it.
- Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e);
+ Log.wtf(TAG, "Could not get URI for " + info.bugreportLocationInfo.mBugreportFile, e);
return null;
}
@@ -1258,7 +1310,7 @@ public class BugreportProgressService extends Service {
new ClipData.Item(null, null, null, bugreportUri));
Log.d(TAG, "share intent: bureportUri=" + bugreportUri);
final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri);
- for (File screenshot : info.screenshotFiles) {
+ for (File screenshot : info.screenshotLocationInfo.mScreenshotFiles) {
final Uri screenshotUri = getUri(context, screenshot);
Log.d(TAG, "share intent: screenshotUri=" + screenshotUri);
clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
@@ -1317,7 +1369,11 @@ public class BugreportProgressService extends Service {
*/
private Intent buildWearWarningIntent() {
Intent intent = new Intent();
- intent.setClassName(mContext, getPackageName() + ".WearBugreportWarningActivity");
+ String systemUIPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUi);
+ String wearBugreportWarningActivity = getResources()
+ .getString(R.string.system_ui_wear_bugreport_warning_activity);
+ intent.setClassName(systemUIPackage, wearBugreportWarningActivity);
if (mContext.getPackageManager().resolveActivity(intent, /* flags */ 0) == null) {
Log.e(TAG, "Cannot find wear bugreport warning activity");
return buildWarningIntent(mContext, /* sendIntent */ null);
@@ -1403,6 +1459,16 @@ public class BugreportProgressService extends Service {
*/
private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
+ final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
+ if (lastUpdate < DELAY_NOTIFICATION_MS) {
+ Log.d(TAG, "Delaying final notification for "
+ + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
+ mMainThreadHandler.postDelayed(() -> {
+ sendBugreportNotification(info, takingScreenshot);
+ }, DELAY_NOTIFICATION_MS - lastUpdate);
+ return;
+ }
+
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(info);
@@ -1423,6 +1489,7 @@ public class BugreportProgressService extends Service {
final Notification.Builder builder = newBaseNotification(mContext)
.setContentTitle(title)
.setTicker(title)
+ .setProgress(100 /* max value of progress percentage */, 100, false)
.setOnlyAlertOnce(false)
.setContentText(content);
@@ -1512,22 +1579,25 @@ public class BugreportProgressService extends Service {
* original in case of failure).
*/
private static void zipBugreport(BugreportInfo info) {
- final String bugreportPath = info.bugreportFile.getAbsolutePath();
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ final String bugreportPath = bugreportFile.getAbsolutePath();
final String zippedPath = bugreportPath.replace(".txt", ".zip");
Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath);
final File bugreportZippedFile = new File(zippedPath);
- try (InputStream is = new FileInputStream(info.bugreportFile);
- ZipOutputStream zos = new ZipOutputStream(
- new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
- addEntry(zos, info.bugreportFile.getName(), is);
+ try (InputStream is = new FileInputStream(bugreportFile);
+ ZipOutputStream zos =
+ new ZipOutputStream(
+ new BufferedOutputStream(
+ new FileOutputStream(bugreportZippedFile)))) {
+ addEntry(zos, bugreportFile.getName(), is);
// Delete old file
- final boolean deleted = info.bugreportFile.delete();
+ final boolean deleted = bugreportFile.delete();
if (deleted) {
Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")");
} else {
Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")");
}
- info.bugreportFile = bugreportZippedFile;
+ info.bugreportLocationInfo.mBugreportFile = bugreportZippedFile;
} catch (IOException e) {
Log.e(TAG, "exception zipping file " + zippedPath, e);
}
@@ -1557,7 +1627,11 @@ public class BugreportProgressService extends Service {
@GuardedBy("mLock")
private void addDetailsToZipFileLocked(BugreportInfo info) {
- if (info.bugreportFile == null) {
+ if (handleBugreportsForWear()) {
+ Log.d(TAG, "Skipping adding details to zipped file");
+ return;
+ }
+ if (info.bugreportLocationInfo.mBugreportFile == null) {
// One possible reason is a bug in the Parcelization code.
Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
return;
@@ -1588,10 +1662,11 @@ public class BugreportProgressService extends Service {
sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
}
- final File dir = info.bugreportFile.getParentFile();
- final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ final File dir = bugreportFile.getParentFile();
+ final File tmpZip = new File(dir, "tmp-" + bugreportFile.getName());
Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
- try (ZipFile oldZip = new ZipFile(info.bugreportFile);
+ try (ZipFile oldZip = new ZipFile(bugreportFile);
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip))) {
// First copy contents from original zip.
@@ -1628,8 +1703,8 @@ public class BugreportProgressService extends Service {
stopForegroundWhenDoneLocked(info.id);
}
- if (!tmpZip.renameTo(info.bugreportFile)) {
- Log.e(TAG, "Could not rename " + tmpZip + " to " + info.bugreportFile);
+ if (!tmpZip.renameTo(bugreportFile)) {
+ Log.e(TAG, "Could not rename " + tmpZip + " to " + bugreportFile);
}
}
@@ -2087,15 +2162,9 @@ public class BugreportProgressService extends Service {
*/
String formattedLastUpdate;
- /**
- * Path of the main bugreport file.
- */
- File bugreportFile;
+ BugreportLocationInfo bugreportLocationInfo;
- /**
- * Path of the screenshot files.
- */
- List<File> screenshotFiles = new ArrayList<>(1);
+ ScreenshotLocationInfo screenshotLocationInfo;
/**
* Whether dumpstate sent an intent informing it has finished.
@@ -2138,10 +2207,17 @@ public class BugreportProgressService extends Service {
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, String baseName, String name,
- @Nullable String shareTitle, @Nullable String shareDescription,
- @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce,
- @Nullable List<Uri> extraAttachments) {
+ BugreportInfo(
+ Context context,
+ String baseName,
+ String name,
+ @Nullable String shareTitle,
+ @Nullable String shareDescription,
+ @BugreportParams.BugreportMode int type,
+ long nonce,
+ @Nullable List<Uri> extraAttachments,
+ BugreportLocationInfo bugreportLocationInfo,
+ ScreenshotLocationInfo screenshotLocationInfo) {
this.context = context;
this.name = this.initialName = name;
this.shareTitle = shareTitle == null ? "" : shareTitle;
@@ -2149,29 +2225,27 @@ public class BugreportProgressService extends Service {
this.type = type;
this.nonce = nonce;
this.baseName = baseName;
- this.bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+ this.bugreportLocationInfo = bugreportLocationInfo;
+ this.screenshotLocationInfo = screenshotLocationInfo;
this.extraAttachments = extraAttachments;
}
- void createBugreportFile() {
- createReadWriteFile(bugreportFile);
- }
-
- void createScreenshotFile(File bugreportsDir) {
+ void maybeCreateScreenshotFile(File bugreportsDir) {
+ if (screenshotLocationInfo.mScreenshotUri != null) {
+ // Screenshot file was already created.
+ return;
+ }
File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
addScreenshot(screenshotFile);
createReadWriteFile(screenshotFile);
}
ParcelFileDescriptor getBugreportFd() {
- return getFd(bugreportFile);
+ return bugreportLocationInfo.getBugreportFd(context);
}
ParcelFileDescriptor getDefaultScreenshotFd() {
- if (screenshotFiles.isEmpty()) {
- return null;
- }
- return getFd(screenshotFiles.get(0));
+ return screenshotLocationInfo.getScreenshotFd(context);
}
void setTitle(String title) {
@@ -2229,14 +2303,14 @@ public class BugreportProgressService extends Service {
* Saves the location of a taken screenshot so it can be sent out at the end.
*/
void addScreenshot(File screenshot) {
- screenshotFiles.add(screenshot);
+ screenshotLocationInfo.mScreenshotFiles.add(screenshot);
}
/**
* Deletes all screenshots taken for a given bugreport.
*/
private void deleteScreenshots() {
- for (File file : screenshotFiles) {
+ for (File file : screenshotLocationInfo.mScreenshotFiles) {
Log.i(TAG, "Deleting screenshot file " + file);
file.delete();
}
@@ -2246,18 +2320,14 @@ public class BugreportProgressService extends Service {
* Deletes bugreport file for a given bugreport.
*/
private void deleteBugreportFile() {
- Log.i(TAG, "Deleting bugreport file " + bugreportFile);
- bugreportFile.delete();
+ bugreportLocationInfo.maybeDeleteBugreportFile();
}
/**
* Deletes empty files for a given bugreport.
*/
private void deleteEmptyFiles() {
- if (bugreportFile.length() == 0) {
- Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
- bugreportFile.delete();
- }
+ bugreportLocationInfo.maybeDeleteEmptyBugreport();
deleteEmptyScreenshots();
}
@@ -2265,14 +2335,7 @@ public class BugreportProgressService extends Service {
* Deletes empty screenshot files.
*/
private void deleteEmptyScreenshots() {
- screenshotFiles.removeIf(file -> {
- final long length = file.length();
- if (length == 0) {
- Log.i(TAG, "Deleting empty screenshot file: " + file);
- file.delete();
- }
- return length == 0;
- });
+ screenshotLocationInfo.deleteEmptyScreenshots();
}
/**
@@ -2280,43 +2343,14 @@ public class BugreportProgressService extends Service {
* {@code initialName} if user has changed it.
*/
void renameScreenshots() {
- deleteEmptyScreenshots();
- if (TextUtils.isEmpty(name) || screenshotFiles.isEmpty()) {
- return;
- }
- final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
- for (File oldFile : screenshotFiles) {
- final String oldName = oldFile.getName();
- final String newName = oldName.replaceFirst(initialName, name);
- final File newFile;
- if (!newName.equals(oldName)) {
- final File renamedFile = new File(oldFile.getParentFile(), newName);
- Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
- newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
- } else {
- Log.w(TAG, "Name didn't change: " + oldName);
- newFile = oldFile;
- }
- if (newFile.length() > 0) {
- renamedFiles.add(newFile);
- } else if (newFile.delete()) {
- Log.d(TAG, "screenshot file: " + newFile + " deleted successfully.");
- }
- }
- screenshotFiles = renamedFiles;
+ screenshotLocationInfo.renameScreenshots(initialName, name);
}
/**
* Rename bugreport file to include the name given by user via UI
*/
void renameBugreportFile() {
- File newBugreportFile = new File(bugreportFile.getParentFile(),
- getFileName(this, ".zip"));
- if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
- if (bugreportFile.renameTo(newBugreportFile)) {
- bugreportFile = newBugreportFile;
- }
- }
+ bugreportLocationInfo.maybeRenameBugreportFile(this);
}
String getFormattedLastUpdate() {
@@ -2349,16 +2383,23 @@ public class BugreportProgressService extends Service {
builder.append("(").append(description.length()).append(" chars)");
}
- return builder
- .append("\n\tfile: ").append(bugreportFile)
- .append("\n\tscreenshots: ").append(screenshotFiles)
- .append("\n\tprogress: ").append(progress)
- .append("\n\tlast_update: ").append(getFormattedLastUpdate())
- .append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
- .append(" addedDetailsToZip: ").append(addedDetailsToZip)
- .append("\n\tshareDescription: ").append(shareDescription)
- .append("\n\tshareTitle: ").append(shareTitle)
- .toString();
+ return builder.append("\n\tfile: ")
+ .append(bugreportLocationInfo)
+ .append("\n\tscreenshots: ")
+ .append(screenshotLocationInfo)
+ .append("\n\tprogress: ")
+ .append(progress)
+ .append("\n\tlast_update: ")
+ .append(getFormattedLastUpdate())
+ .append("\n\taddingDetailsToZip: ")
+ .append(addingDetailsToZip)
+ .append(" addedDetailsToZip: ")
+ .append(addedDetailsToZip)
+ .append("\n\tshareDescription: ")
+ .append(shareDescription)
+ .append("\n\tshareTitle: ")
+ .append(shareTitle)
+ .toString();
}
// Parcelable contract
@@ -2375,11 +2416,12 @@ public class BugreportProgressService extends Service {
lastProgress.set(in.readInt());
lastUpdate.set(in.readLong());
formattedLastUpdate = in.readString();
- bugreportFile = readFile(in);
+ bugreportLocationInfo = new BugreportLocationInfo(readFile(in));
int screenshotSize = in.readInt();
for (int i = 1; i <= screenshotSize; i++) {
- screenshotFiles.add(readFile(in));
+ screenshotLocationInfo = new ScreenshotLocationInfo(null);
+ screenshotLocationInfo.mScreenshotFiles.add(readFile(in));
}
finished.set(in.readInt() == 1);
@@ -2404,10 +2446,10 @@ public class BugreportProgressService extends Service {
dest.writeInt(lastProgress.intValue());
dest.writeLong(lastUpdate.longValue());
dest.writeString(getFormattedLastUpdate());
- writeFile(dest, bugreportFile);
+ writeFile(dest, bugreportLocationInfo.mBugreportFile);
- dest.writeInt(screenshotFiles.size());
- for (File screenshotFile : screenshotFiles) {
+ dest.writeInt(screenshotLocationInfo.mScreenshotFiles.size());
+ for (File screenshotFile : screenshotLocationInfo.mScreenshotFiles) {
writeFile(dest, screenshotFile);
}
@@ -2449,6 +2491,261 @@ public class BugreportProgressService extends Service {
};
}
+ /**
+ * Class for abstracting bugreport location. There are two possible cases:
+ * <li>If a bugreport request included a URI for bugreports of type {@link
+ * BugreportParams.BUGREPORT_MODE_WEAR}, then the URI file descriptor will be used. The
+ * requesting app manages the creation and lifecycle of the file.
+ * <li>If no URI is provided in the bugreport request, Shell will create a bugreport file and
+ * manage its lifecycle.
+ */
+ private static final class BugreportLocationInfo {
+ /** Path of the main bugreport file. */
+ @Nullable private File mBugreportFile;
+
+ /** Uri to bugreport location. */
+ @Nullable private Uri mBugreportUri;
+
+ BugreportLocationInfo(File bugreportFile) {
+ this.mBugreportFile = bugreportFile;
+ }
+
+ BugreportLocationInfo(Uri bugreportUri, File bugreportsDir, String baseName, String name) {
+ if (bugreportUri != null) {
+ this.mBugreportUri = bugreportUri;
+ } else {
+ this.mBugreportFile = new File(bugreportsDir, getFileName(".zip", baseName, name));
+ }
+ }
+
+ private boolean maybeCreateBugreportFile() {
+ if (mBugreportFile != null && mBugreportFile.exists()) {
+ Log.e(
+ TAG,
+ "Failed to start bugreport generation, the requested bugreport file "
+ + mBugreportFile
+ + " already exists");
+ return false;
+ }
+ createBugreportFile();
+ return true;
+ }
+
+ private void createBugreportFile() {
+ if (mBugreportUri == null) {
+ createReadWriteFile(mBugreportFile);
+ }
+ }
+
+ private ParcelFileDescriptor getBugreportFd(Context context) {
+ if (mBugreportUri != null) {
+ try {
+ return context.getContentResolver()
+ .openFileDescriptor(mBugreportUri, WRITE_AND_APPEND_MODE);
+ } catch (Exception e) {
+ Log.d(TAG, "Faced exception when getting BR file descriptor", e);
+ return null;
+ }
+ }
+ if (mBugreportFile == null) {
+ Log.e(TAG, "Could not get bugreport file descriptor; bugreport file was null");
+ return null;
+ }
+ return getFd(mBugreportFile);
+ }
+
+ private void maybeDeleteBugreportFile() {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's
+ // lifecycle.
+ return;
+ }
+ Log.i(TAG, "Deleting bugreport file " + mBugreportFile);
+ mBugreportFile.delete();
+ }
+
+ private boolean isValidBugreportResult() {
+ if (mBugreportFile != null) {
+ return mBugreportFile.exists() && mBugreportFile.canRead();
+ }
+ // If a bugreport uri was provided, we can't assert on whether the file exists and can
+ // be read. Assume the result is valid.
+ return true;
+ }
+
+ private void maybeDeleteEmptyBugreport() {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's
+ // lifecycle.
+ return;
+ }
+ if (mBugreportFile.length() == 0) {
+ Log.i(TAG, "Deleting empty bugreport file: " + mBugreportFile);
+ mBugreportFile.delete();
+ }
+ }
+
+ private void maybeRenameBugreportFile(BugreportInfo bugreportInfo) {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's naming.
+ return;
+ }
+ File newBugreportFile =
+ new File(mBugreportFile.getParentFile(), getFileName(bugreportInfo, ".zip"));
+ if (!newBugreportFile.getPath().equals(mBugreportFile.getPath())) {
+ if (mBugreportFile.renameTo(newBugreportFile)) {
+ mBugreportFile = newBugreportFile;
+ }
+ }
+ }
+
+ private boolean isPlainText() {
+ if (mBugreportFile != null) {
+ return mBugreportFile.getName().toLowerCase().endsWith(".txt");
+ }
+ return false;
+ }
+
+ private boolean isFileEmpty(Context context) {
+ if (mBugreportFile != null) {
+ return mBugreportFile.length() == 0;
+ }
+ return getBugreportFd(context).getStatSize() == 0;
+ }
+
+ @Override
+ public String toString() {
+ return "BugreportLocationInfo{"
+ + "bugreportFile="
+ + mBugreportFile
+ + ", bugreportUri="
+ + mBugreportUri
+ + '}';
+ }
+
+ private String getBugreportPath() {
+ if (mBugreportUri != null) {
+ return mBugreportUri.getLastPathSegment();
+ }
+ return mBugreportFile.getAbsolutePath();
+ }
+ }
+
+ /**
+ * Class for abstracting screenshot location. There are two possible cases:
+ * <li>If a bugreport request included a URI for bugreports of type {@link
+ * BugreportParams.BUGREPORT_MODE_WEAR}, then the URI file descriptor will be used. The
+ * requesting app manages the creation and lifecycle of the file.
+ * <li>If no URI is provided in the bugreport request, Shell will create the screenshot file and
+ * manage its lifecycle.
+ */
+ private static final class ScreenshotLocationInfo {
+
+ /** Uri to screenshot location. */
+ @Nullable private Uri mScreenshotUri;
+
+ /** Path to screenshot files. */
+ private List<File> mScreenshotFiles = new ArrayList<>(1);
+
+ ScreenshotLocationInfo(Uri screenshotUri) {
+ if (screenshotUri != null) {
+ this.mScreenshotUri = screenshotUri;
+ }
+ }
+
+ private ParcelFileDescriptor getScreenshotFd(Context context) {
+ if (mScreenshotUri != null) {
+ try {
+ return context.getContentResolver()
+ .openFileDescriptor(mScreenshotUri, WRITE_AND_APPEND_MODE);
+ } catch (Exception e) {
+ Log.d(TAG, "Faced exception when getting screenshot file", e);
+ return null;
+ }
+ }
+
+ if (mScreenshotFiles.isEmpty()) {
+ return null;
+ }
+ return getFd(mScreenshotFiles.getFirst());
+ }
+
+ @Override
+ public String toString() {
+ return "ScreenshotLocationInfo{"
+ + "screenshotUri="
+ + mScreenshotUri
+ + ", screenshotFiles="
+ + mScreenshotFiles
+ + '}';
+ }
+
+ private String getScreenshotPath() {
+ if (mScreenshotUri != null) {
+ return mScreenshotUri.getLastPathSegment();
+ }
+ return getScreenshotForIntent();
+ }
+
+ private void renameScreenshots(String initialName, String name) {
+ if (mScreenshotUri != null) {
+ // If a screenshot uri is provided, then shell is not responsible for the
+ // screenshot's naming.
+ return;
+ }
+ deleteEmptyScreenshots();
+ if (TextUtils.isEmpty(name) || mScreenshotFiles.isEmpty()) {
+ // If there is no user set name for screenshot file or there are no screenshot
+ // files, there's nothing to do.
+ return;
+ }
+ final List<File> renamedFiles = new ArrayList<>(mScreenshotFiles.size());
+ for (File oldFile : mScreenshotFiles) {
+ final String oldName = oldFile.getName();
+ final String newName = oldName.replaceFirst(initialName, name);
+ final File newFile;
+ if (!newName.equals(oldName)) {
+ final File renamedFile = new File(oldFile.getParentFile(), newName);
+ Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
+ newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
+ } else {
+ Log.w(TAG, "Name didn't change: " + oldName);
+ newFile = oldFile;
+ }
+ if (newFile.length() > 0) {
+ renamedFiles.add(newFile);
+ } else if (newFile.delete()) {
+ Log.d(TAG, "screenshot file: " + newFile + " deleted successfully.");
+ }
+ }
+ mScreenshotFiles = renamedFiles;
+ }
+
+ private void deleteEmptyScreenshots() {
+ mScreenshotFiles.removeIf(
+ file -> {
+ final long length = file.length();
+ if (length == 0) {
+ Log.i(TAG, "Deleting empty screenshot file: " + file);
+ file.delete();
+ }
+ return length == 0;
+ });
+ }
+
+ /**
+ * Checks if screenshot array is non-empty and returns the first screenshot's path. The
+ * first screenshot is the default screenshot for the bugreport types that take it.
+ */
+ private String getScreenshotForIntent() {
+ if (!mScreenshotFiles.isEmpty()) {
+ final File screenshotFile = mScreenshotFiles.getFirst();
+ return screenshotFile.getAbsolutePath();
+ }
+ return null;
+ }
+ }
+
@GuardedBy("mLock")
private void checkProgressUpdatedLocked(BugreportInfo info, int progress) {
if (progress > CAPPED_PROGRESS) {
@@ -2461,7 +2758,6 @@ public class BugreportProgressService extends Service {
}
}
info.progress.set(progress);
- info.lastUpdate.set(System.currentTimeMillis());
updateProgress(info);
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 050a3704df1f..7bda2ea790b0 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -193,7 +193,7 @@ public class BugreportReceiverTest {
mService.mScreenshotDelaySec = SCREENSHOT_DELAY_SECONDS;
// Dup the fds which are passing to startBugreport function.
Mockito.doAnswer(invocation -> {
- final boolean isScreenshotRequested = invocation.getArgument(6);
+ final boolean isScreenshotRequested = invocation.getArgument(7);
if (isScreenshotRequested) {
mScreenshotFd = ParcelFileDescriptor.dup(invocation.getArgument(3));
}
@@ -250,7 +250,22 @@ public class BugreportReceiverTest {
mIDumpstateListener.onProgress(300);
assertProgressNotification(mProgressTitle, 99);
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 1);
+ assertActionSendMultiple(extras);
+
+ assertServiceNotRunning();
+ }
+
+ @Test
+ public void testStressProgress() throws Exception {
+ sendBugreportStarted();
+ waitForScreenshotButtonEnabled(true);
+
+ for (int i = 0; i <= 1000; i++) {
+ mIDumpstateListener.onProgress(i);
+ }
+ sendBugreportFinished();
+ Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
assertActionSendMultiple(extras);
assertServiceNotRunning();
@@ -277,7 +292,7 @@ public class BugreportReceiverTest {
assertScreenshotButtonEnabled(false);
waitForScreenshotButtonEnabled(true);
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 2);
assertActionSendMultiple(extras, NO_NAME, NO_TITLE, NO_DESCRIPTION, 1);
assertServiceNotRunning();
@@ -294,7 +309,7 @@ public class BugreportReceiverTest {
// There's no indication in the UI about the screenshot finish, so just sleep like a baby...
sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
- Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+ Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 2);
assertActionSendMultiple(extras, NO_NAME, NO_TITLE, NO_DESCRIPTION, 1);
assertServiceNotRunning();
@@ -328,7 +343,7 @@ public class BugreportReceiverTest {
assertProgressNotification(NEW_NAME, 00.00f);
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE, 1);
assertActionSendMultiple(extras, NEW_NAME, TITLE, mDescription, 0);
assertServiceNotRunning();
@@ -363,7 +378,7 @@ public class BugreportReceiverTest {
assertProgressNotification(NEW_NAME, 00.00f);
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE, 1);
assertActionSendMultiple(extras, NEW_NAME, TITLE, mDescription, 0);
assertServiceNotRunning();
@@ -390,7 +405,7 @@ public class BugreportReceiverTest {
detailsUi.descField.setText(mDescription);
detailsUi.clickOk();
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 1);
assertActionSendMultiple(extras, NO_NAME, NO_TITLE, mDescription, 0);
assertServiceNotRunning();
@@ -441,7 +456,7 @@ public class BugreportReceiverTest {
detailsUi.clickOk();
// Finally, share bugreport.
- Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+ Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
assertActionSendMultiple(extras, NO_NAME, TITLE, mDescription, 0);
assertServiceNotRunning();
@@ -504,7 +519,7 @@ public class BugreportReceiverTest {
mUiBot.click(ok, "ok");
// Share the bugreport.
- mUiBot.chooseActivity(UI_NAME);
+ mUiBot.chooseActivity(UI_NAME, mContext, 1);
Bundle extras = mListener.getExtras();
assertActionSendMultiple(extras);
@@ -531,7 +546,7 @@ public class BugreportReceiverTest {
sendBugreportFinished();
killService();
assertServiceNotRunning();
- Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+ Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
assertActionSendMultiple(extras);
}
@@ -618,45 +633,49 @@ public class BugreportReceiverTest {
* Sends a "bugreport finished" event and waits for the result.
*
* @param id The bugreport id for finished notification string title substitution.
+ * @param count Number of files to be shared
* @return extras sent in the shared intent.
*/
- private Bundle sendBugreportFinishedAndGetSharedIntent(int id) throws Exception {
+ private Bundle sendBugreportFinishedAndGetSharedIntent(int id, int count) throws Exception {
sendBugreportFinished();
- return acceptBugreportAndGetSharedIntent(id);
+ return acceptBugreportAndGetSharedIntent(id, count);
}
/**
* Sends a "bugreport finished" event and waits for the result.
*
* @param notificationTitle The title of finished notification.
+ * @param count Number of files to be shared
* @return extras sent in the shared intent.
*/
- private Bundle sendBugreportFinishedAndGetSharedIntent(String notificationTitle)
+ private Bundle sendBugreportFinishedAndGetSharedIntent(String notificationTitle, int count)
throws Exception {
sendBugreportFinished();
- return acceptBugreportAndGetSharedIntent(notificationTitle);
+ return acceptBugreportAndGetSharedIntent(notificationTitle, count);
}
/**
* Accepts the notification to share the finished bugreport and waits for the result.
*
* @param id The bugreport id for finished notification string title substitution.
+ * @param count Number of files to be shared
* @return extras sent in the shared intent.
*/
- private Bundle acceptBugreportAndGetSharedIntent(int id) {
+ private Bundle acceptBugreportAndGetSharedIntent(int id, int count) {
final String notificationTitle = mContext.getString(R.string.bugreport_finished_title, id);
- return acceptBugreportAndGetSharedIntent(notificationTitle);
+ return acceptBugreportAndGetSharedIntent(notificationTitle, count);
}
/**
* Accepts the notification to share the finished bugreport and waits for the result.
*
* @param notificationTitle The title of finished notification.
+ * @param count Number of files to be shared
* @return extras sent in the shared intent.
*/
- private Bundle acceptBugreportAndGetSharedIntent(String notificationTitle) {
+ private Bundle acceptBugreportAndGetSharedIntent(String notificationTitle, int count) {
mUiBot.clickOnNotification(notificationTitle);
- mUiBot.chooseActivity(UI_NAME);
+ mUiBot.chooseActivity(UI_NAME, mContext, count);
return mListener.getExtras();
}
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index ce9f70d8b977..60008a353d81 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -18,9 +18,12 @@ package com.android.shell;
import android.app.Instrumentation;
import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.res.Resources;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.PluralsMessageFormatter;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
@@ -34,7 +37,9 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* A helper class for UI-related testing tasks.
@@ -206,11 +211,26 @@ final class UiBot {
*
* @param name name of the activity as displayed in the UI (typically the value set by
* {@code android:label} in the manifest).
+ * @param context Context of the target application
+ * @param count Number of files to be shared
*/
- public void chooseActivity(String name) {
+ public void chooseActivity(String name, Context context, int count) {
// It uses an intent chooser now, so just getting the activity by text is enough...
- final String share = mInstrumentation.getContext().getString(
- com.android.internal.R.string.share);
+ Resources res = null;
+ try {
+ res = context.getPackageManager()
+ .getResourcesForApplication("com.android.intentresolver");
+ } catch (Exception e) {
+ assertNotNull("could not get resources for com.android.intentresolver", res);
+ }
+ /* Resource read is defined as a string which contains a plural
+ * which needs some formatting */
+ Map<String, Object> arguments = new HashMap<>();
+ arguments.put("count", count);
+ final String share = PluralsMessageFormatter.format(
+ res,
+ arguments,
+ res.getIdentifier("sharing_files", "string", "com.android.intentresolver"));
boolean gotIt = mDevice.wait(Until.hasObject(By.text(share)), mTimeout);
assertTrue("could not get share activity (" + share + ")", gotIt);
swipeUp();
diff --git a/packages/SimAppDialog/res/values-af/strings.xml b/packages/SimAppDialog/res/values-af/strings.xml
index 143bc67d0794..7a2ed82f37eb 100644
--- a/packages/SimAppDialog/res/values-af/strings.xml
+++ b/packages/SimAppDialog/res/values-af/strings.xml
@@ -19,8 +19,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="8898068901680117589">"SIM-programdialoog"</string>
<string name="install_carrier_app_title" msgid="334729104862562585">"Aktiveer mobiele diens"</string>
- <string name="install_carrier_app_description" msgid="4014303558674923797">"Jy sal die <xliff:g id="ID_1">%1$s</xliff:g>-program moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
+ <string name="install_carrier_app_description" msgid="4014303558674923797">"Jy sal die <xliff:g id="ID_1">%1$s</xliff:g>-app moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
<string name="install_carrier_app_description_default" msgid="7356830245205847840">"Jy sal die diensverskafferprogram moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Nie nou nie"</string>
- <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Laai program af"</string>
+ <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Laai app af"</string>
</resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e0117368515b..31d5bfaccd5d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -322,6 +322,9 @@
<!-- Query all packages on device on R+ -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <!-- Query advanced protection state -->
+ <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" />
+
<queries>
<intent>
<action android:name="android.intent.action.CREATE_NOTE" />
@@ -385,6 +388,9 @@
is ready -->
<uses-permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
+ <!-- To be able to decipher default applications for certain roles in shortcut helper -->
+ <uses-permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -459,7 +465,7 @@
android:label="@string/screenshot_scroll_label"
android:finishOnTaskLaunch="true" />
- <service android:name=".screenshot.ScreenshotProxyService"
+ <service android:name=".screenshot.proxy.ScreenshotProxyService"
android:permission="com.android.systemui.permission.SELF"
android:exported="false" />
@@ -490,6 +496,7 @@
<activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity"
android:exported="true"
android:showForAllUsers="true"
+ android:excludeFromRecents="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
@@ -500,6 +507,7 @@
<activity android:name=".inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity"
android:exported="true"
android:showForAllUsers="true"
+ android:excludeFromRecents="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
index 10e36b872b04..9506a7e381b9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
@@ -11,7 +11,7 @@
<string name="recent_apps_label" msgid="6583276995616385847">"हालै चलाइएका एप"</string>
<string name="lockscreen_label" msgid="648347953557887087">"लक स्क्रिन"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"द्रुत सेटिङहरू"</string>
- <string name="notifications_label" msgid="6829741046963013567">"सूचनाहरू"</string>
+ <string name="notifications_label" msgid="6829741046963013567">"नोटिफिकेसनहरू"</string>
<string name="screenshot_label" msgid="863978141223970162">"स्क्रिनसट"</string>
<string name="screenshot_utterance" msgid="1430760563401895074">"स्क्रिनसट लिनुहोस्"</string>
<string name="volume_up_label" msgid="8592766918780362870">"भोल्युम बढाउनुहोस्"</string>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c1f786826922..755b4542b759 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -679,7 +679,7 @@ flag {
flag {
name: "volume_redesign"
namespace: "systemui"
- description: "Enables Volume BC25 visuals update"
+ description: "Enables Volume visuals update"
bug: "368308908"
}
@@ -1230,13 +1230,6 @@ flag {
}
flag {
- name: "communal_hub_on_mobile"
- namespace: "systemui"
- description: "Brings the glanceable hub experience to mobile phones"
- bug: "375689917"
-}
-
-flag {
name: "glanceable_hub_v2"
namespace: "systemui"
description: "Gates the refreshed glanceable hub experience that also brings the glanceable hub to mobile phones"
@@ -1332,7 +1325,7 @@ flag {
name: "media_controls_ui_update"
namespace: "systemui"
description: "Enables media visuals update"
- bug: "380053768"
+ bug: "379044958"
}
flag {
@@ -1346,16 +1339,6 @@ flag {
}
flag {
- name: "validate_keyboard_shortcut_helper_icon_uri"
- namespace: "systemui"
- description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper."
- bug: "331180422"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "glanceable_hub_allow_keyguard_when_dreaming"
namespace: "systemui"
description: "Allows users to exit dream to keyguard with glanceable hub enabled"
@@ -1886,3 +1869,24 @@ flag {
description: "Larger privacy indicators on large screen"
bug: "381864715"
}
+
+flag {
+ name: "desktop_effects_qs_tile"
+ namespace: "systemui"
+ description: "Enables the QS tile for desktop effects"
+ bug: "376797327"
+}
+
+flag {
+ name: "hub_edit_mode_touch_adjustments"
+ namespace: "systemui"
+ description: "Makes selected widget toggleable in edit mode and modifier buttons mutually exclusive."
+ bug: "383160667"
+}
+
+flag {
+ name: "glanceable_hub_direct_edit_mode"
+ namespace: "systemui"
+ description: "Invokes edit mode directly from long press in glanceable hub"
+ bug: "382531177"
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index cec740ad899b..d0404ec02306 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -18,8 +18,11 @@ package com.android.systemui.animation;
import android.annotation.Nullable;
import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
@@ -44,6 +47,9 @@ import java.util.concurrent.Executor;
public class ViewUIComponent implements UIComponent {
private static final String TAG = "ViewUIComponent";
private static final boolean DEBUG = Build.IS_USERDEBUG || Log.isLoggable(TAG, Log.DEBUG);
+ private final Path mClippingPath = new Path();
+ private final Outline mClippingOutline = new Outline();
+
private final OnDrawListener mOnDrawListener = this::postDraw;
private final View mView;
@@ -57,6 +63,14 @@ public class ViewUIComponent implements UIComponent {
mView = view;
}
+ /**
+ * @return the view wrapped by this UI component.
+ * @hide
+ */
+ public View getView() {
+ return mView;
+ }
+
@Override
public float getAlpha() {
return mView.getAlpha();
@@ -174,6 +188,17 @@ public class ViewUIComponent implements UIComponent {
canvas.scale(
(float) renderBounds.width() / realBounds.width(),
(float) renderBounds.height() / realBounds.height());
+
+ if (mView.getClipToOutline()) {
+ mView.getOutlineProvider().getOutline(mView, mClippingOutline);
+ mClippingPath.reset();
+ RectF rect = new RectF(0, 0, mView.getWidth(), mView.getHeight());
+ final float cornerRadius = mClippingOutline.getRadius();
+ mClippingPath.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW);
+ mClippingPath.close();
+ canvas.clipPath(mClippingPath);
+ }
+
canvas.saveLayerAlpha(null, (int) (255 * mView.getAlpha()));
mView.draw(canvas);
canvas.restore();
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 3eeaf41d874a..41a00f5237f7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -873,6 +873,9 @@ constructor(
) {
// Raise closing task to "above" layer so it isn't covered.
t.setLayer(target.leash, aboveLayers - i)
+ } else if (TransitionUtil.isOpeningType(change.mode)) {
+ // Put into the "below" layer space.
+ t.setLayer(target.leash, belowLayers - i)
}
} else if (TransitionInfo.isIndependent(change, info)) {
// Root tasks
@@ -1153,7 +1156,7 @@ constructor(
// If a [controller.windowAnimatorState] exists, treat this like a takeover.
takeOverAnimationInternal(
window,
- startWindowStates = null,
+ startWindowState = null,
startTransaction = null,
callback,
)
@@ -1168,22 +1171,23 @@ constructor(
callback: IRemoteAnimationFinishedCallback?,
) {
val window = setUpAnimation(apps, callback) ?: return
- takeOverAnimationInternal(window, startWindowStates, startTransaction, callback)
+ val startWindowState = startWindowStates[apps!!.indexOf(window)]
+ takeOverAnimationInternal(window, startWindowState, startTransaction, callback)
}
private fun takeOverAnimationInternal(
window: RemoteAnimationTarget,
- startWindowStates: Array<WindowAnimationState>?,
+ startWindowState: WindowAnimationState?,
startTransaction: SurfaceControl.Transaction?,
callback: IRemoteAnimationFinishedCallback?,
) {
val useSpring =
- !controller.isLaunching && startWindowStates != null && startTransaction != null
+ !controller.isLaunching && startWindowState != null && startTransaction != null
startAnimation(
window,
navigationBar = null,
useSpring,
- startWindowStates,
+ startWindowState,
startTransaction,
callback,
)
@@ -1293,7 +1297,7 @@ constructor(
window: RemoteAnimationTarget,
navigationBar: RemoteAnimationTarget? = null,
useSpring: Boolean = false,
- startingWindowStates: Array<WindowAnimationState>? = null,
+ startingWindowState: WindowAnimationState? = null,
startTransaction: SurfaceControl.Transaction? = null,
iCallback: IRemoteAnimationFinishedCallback? = null,
) {
@@ -1339,6 +1343,7 @@ constructor(
val isExpandingFullyAbove =
transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState)
+ val windowState = startingWindowState ?: controller.windowAnimatorState
// We animate the opening window and delegate the view expansion to [this.controller].
val delegate = this.controller
@@ -1361,18 +1366,6 @@ constructor(
}
}
- // The states are sorted matching the changes inside the transition info.
- // Using this info, the RemoteAnimationTargets are created, with their
- // prefixOrderIndex fields in reverse order to that of changes. To extract
- // the right state, we need to invert again.
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[
- startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
-
// TODO(b/323863002): use the timestamp and velocity to update the initial
// position.
val bounds = windowState?.bounds
@@ -1461,12 +1454,6 @@ constructor(
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
}
}
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
val velocityPxPerS =
if (longLivedReturnAnimationsEnabled() && windowState?.velocityPxPerMs != null) {
val xVelocityPxPerS = windowState.velocityPxPerMs.x * 1000
@@ -1485,6 +1472,7 @@ constructor(
fadeWindowBackgroundLayer = !controller.isBelowAnimatingWindow,
drawHole = !controller.isBelowAnimatingWindow,
startVelocity = velocityPxPerS,
+ startFrameTime = windowState?.timestamp ?: -1,
)
}
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 558c1eba2c1c..65cd3c79cd16 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -397,6 +397,10 @@ constructor(
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
}
+
+ if (isEphemeral) {
+ onDispose()
+ }
}
companion object {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index e2bc4095e1b5..4e889e946a5f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -27,6 +27,8 @@ import android.graphics.drawable.GradientDrawable
import android.util.FloatProperty
import android.util.Log
import android.util.MathUtils
+import android.util.TimeUtils
+import android.view.Choreographer
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
@@ -366,6 +368,7 @@ class TransitionAnimator(
@get:VisibleForTesting val springY: SpringAnimation,
@get:VisibleForTesting val springScale: SpringAnimation,
private val springState: SpringState,
+ private val startFrameTime: Long,
private val onAnimationStart: Runnable,
) : Animation {
@get:VisibleForTesting
@@ -374,6 +377,42 @@ class TransitionAnimator(
override fun start() {
onAnimationStart.run()
+
+ // If no start frame time is provided, we start the springs normally.
+ if (startFrameTime < 0) {
+ startSprings()
+ return
+ }
+
+ // This function is not guaranteed to be called inside a frame. We try to access the
+ // frame time immediately, but if we're not inside a frame this will throw an exception.
+ // We must then post a callback to be run at the beginning of the next frame.
+ try {
+ initAndStartSprings(Choreographer.getInstance().frameTime)
+ } catch (_: IllegalStateException) {
+ Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
+ initAndStartSprings(frameTimeNanos / TimeUtils.NANOS_PER_MS)
+ }
+ }
+ }
+
+ private fun initAndStartSprings(frameTime: Long) {
+ // Initialize the spring as if it had started at the time that its start state
+ // was created.
+ springX.doAnimationFrame(startFrameTime)
+ springY.doAnimationFrame(startFrameTime)
+ springScale.doAnimationFrame(startFrameTime)
+ // Move the spring time forward to the current frame, so it updates its internal state
+ // following the initial momentum over the elapsed time.
+ springX.doAnimationFrame(frameTime)
+ springY.doAnimationFrame(frameTime)
+ springScale.doAnimationFrame(frameTime)
+ // Actually start the spring. We do this after the previous calls because the framework
+ // doesn't like it when you call doAnimationFrame() after start() with an earlier time.
+ startSprings()
+ }
+
+ private fun startSprings() {
springX.start()
springY.start()
springScale.start()
@@ -471,7 +510,9 @@ class TransitionAnimator(
* is true.
*
* If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation
- * using it for the initial momentum will be used instead of the default interpolators.
+ * using it for the initial momentum will be used instead of the default interpolators. In this
+ * case, [startFrameTime] (if non-negative) represents the frame time at which the springs
+ * should be started.
*/
fun startAnimation(
controller: Controller,
@@ -480,6 +521,7 @@ class TransitionAnimator(
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
if (!controller.isLaunching) assertReturnAnimations()
if (startVelocity != null) assertLongLivedReturnAnimations()
@@ -502,6 +544,7 @@ class TransitionAnimator(
fadeWindowBackgroundLayer,
drawHole,
startVelocity,
+ startFrameTime,
)
.apply { start() }
}
@@ -515,6 +558,7 @@ class TransitionAnimator(
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -537,6 +581,7 @@ class TransitionAnimator(
startState,
endState,
startVelocity,
+ startFrameTime,
windowBackgroundLayer,
transitionContainer,
transitionContainerOverlay,
@@ -722,6 +767,7 @@ class TransitionAnimator(
startState: State,
endState: State,
startVelocity: PointF,
+ startFrameTime: Long,
windowBackgroundLayer: GradientDrawable,
transitionContainer: View,
transitionContainerOverlay: ViewGroupOverlay,
@@ -912,7 +958,7 @@ class TransitionAnimator(
}
}
- return MultiSpringAnimation(springX, springY, springScale, springState) {
+ return MultiSpringAnimation(springX, springY, springScale, springState, startFrameTime) {
onAnimationStart(
controller,
isExpandingFullyAbove,
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
index 2f83d82bbec7..2a27a3033cf9 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
@@ -25,6 +25,7 @@ import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiParameter
import org.jetbrains.uast.UClass
import org.jetbrains.uast.getContainingUFile
@@ -40,14 +41,8 @@ class ShadeDisplayAwareDetector : Detector(), SourceCodeScanner {
if (!isInRelevantShadePackage(node)) continue
if (IGNORED_PACKAGES.contains(node.qualifiedName)) continue
- // Check the any context-dependent parameter to see if it has @ShadeDisplayAware
- // annotation
for (parameter in constructor.parameterList.parameters) {
- val shouldReport =
- CONTEXT_DEPENDENT_SHADE_CLASSES.contains(
- parameter.type.canonicalText
- ) && !parameter.hasAnnotation(SHADE_DISPLAY_AWARE_ANNOTATION)
- if (shouldReport) {
+ if (parameter.shouldReport()) {
context.report(
issue = ISSUE,
scope = parameter.declarationScope,
@@ -62,20 +57,35 @@ class ShadeDisplayAwareDetector : Detector(), SourceCodeScanner {
companion object {
private const val INJECT_ANNOTATION = "javax.inject.Inject"
+ private const val APPLICATION_ANNOTATION =
+ "com.android.systemui.dagger.qualifiers.Application"
private const val SHADE_DISPLAY_AWARE_ANNOTATION =
"com.android.systemui.shade.ShadeDisplayAware"
+ private const val MAIN_ANNOTATION = "com.android.systemui.dagger.qualifiers.Main"
+
+ private const val CONTEXT = "android.content.Context"
+ private const val WINDOW_MANAGER = "android.view.WindowManager"
+ private const val LAYOUT_INFLATER = "android.view.LayoutInflater"
+ private const val RESOURCES = "android.content.res.Resources"
+ private const val CONFIG_STATE = "com.android.systemui.common.ui.ConfigurationState"
+ private const val CONFIG_CONTROLLER =
+ "com.android.systemui.statusbar.policy.ConfigurationController"
+ private const val CONFIG_INTERACTOR =
+ "com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor"
private val CONTEXT_DEPENDENT_SHADE_CLASSES =
setOf(
- "android.content.Context",
- "android.view.WindowManager",
- "android.view.LayoutInflater",
- "android.content.res.Resources",
- "com.android.systemui.common.ui.ConfigurationState",
- "com.android.systemui.statusbar.policy.ConfigurationController",
- "com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor",
+ CONTEXT,
+ WINDOW_MANAGER,
+ LAYOUT_INFLATER,
+ RESOURCES,
+ CONFIG_STATE,
+ CONFIG_CONTROLLER,
+ CONFIG_INTERACTOR,
)
+ private val CONFIG_CLASSES = setOf(CONFIG_STATE, CONFIG_CONTROLLER, CONFIG_INTERACTOR)
+
private val SHADE_WINDOW_PACKAGES =
listOf(
"com.android.systemui.biometrics",
@@ -93,6 +103,19 @@ class ShadeDisplayAwareDetector : Detector(), SourceCodeScanner {
"com.android.systemui.qs.customize.TileAdapter",
)
+ private fun PsiParameter.shouldReport(): Boolean {
+ val className = type.canonicalText
+
+ // check if the parameter is a context-dependent class relevant to shade
+ if (className !in CONTEXT_DEPENDENT_SHADE_CLASSES) return false
+ if (hasAnnotation(SHADE_DISPLAY_AWARE_ANNOTATION) || hasAnnotation(MAIN_ANNOTATION))
+ return false
+ // check if its a @Application-annotated Context
+ if (className == CONTEXT && hasAnnotation(APPLICATION_ANNOTATION)) return false
+
+ return true
+ }
+
private fun isInRelevantShadePackage(node: UClass): Boolean {
val packageName = node.getContainingUFile()?.packageName
if (packageName.isNullOrBlank()) return false
@@ -102,15 +125,15 @@ class ShadeDisplayAwareDetector : Detector(), SourceCodeScanner {
}
private fun reportMsg(className: String) =
- """UI elements of the shade window
- |should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
- |@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
- |might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
- |If the usage of $className is not related to display specific configuration or UI, then there is
- |technically no need to use the annotation, and you can annotate the class with
- |@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
- |"""
- .trimMargin()
+ "UI elements of the shade window should use " +
+ "ShadeDisplayAware-annotated $className, as the shade might move between windows, " +
+ "and only @ShadeDisplayAware resources are updated with the new configuration " +
+ "correctly. Failures to do so might result in wrong dimensions for shade window " +
+ "classes (e.g. using the wrong density or theme). If the usage of $className is " +
+ "not related to display specific configuration or UI, then there is technically " +
+ "no need to use the annotation, and you can annotate the class with " +
+ "@SuppressLint(\"ShadeDisplayAwareContextChecker\")/" +
+ "@Suppress(\"ShadeDisplayAwareContextChecker\")".trimMargin()
@JvmField
val ISSUE: Issue =
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
index 58ad363f5b57..638d7cb7ee58 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
@@ -63,6 +63,26 @@ class ShadeDisplayAwareDetectorTest : SystemUILintDetectorTest() {
)
.indented()
+ private val applicationStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.dagger.qualifiers
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class Application
+ """
+ )
+ .indented()
+
+ private val mainStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.dagger.qualifiers
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class Main
+ """
+ )
+ .indented()
+
private val configStateStub: TestFile =
kotlin(
"""
@@ -98,6 +118,8 @@ class ShadeDisplayAwareDetectorTest : SystemUILintDetectorTest() {
injectStub,
qsContext,
shadeDisplayAwareStub,
+ applicationStub,
+ mainStub,
configStateStub,
configControllerStub,
configInteractorStub,
@@ -259,6 +281,60 @@ class ShadeDisplayAwareDetectorTest : SystemUILintDetectorTest() {
}
@Test
+ fun injectedConstructor_inRelevantPackage_withApplicationAnnotatedContext() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+ import com.android.systemui.dagger.qualifiers.Application
+
+ class ExampleClass
+ @Inject
+ constructor(@Application private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withMainAnnotatedConfigurationClass() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import com.android.systemui.common.ui.ConfigurationState
+ import com.android.systemui.dagger.qualifiers.Main
+
+ class ExampleClass
+ @Inject
+ constructor(@Main private val configState: ConfigurationState)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
fun injectedConstructor_notInRelevantPackage_withRelevantParameter_withoutAnnotation() {
lint()
.files(
@@ -363,13 +439,13 @@ class ShadeDisplayAwareDetectorTest : SystemUILintDetectorTest() {
}
private fun errorMsgString(lineNumber: Int, className: String) =
- """
- src/com/android/systemui/shade/example/ExampleClass.kt:$lineNumber: Error: UI elements of the shade window
- should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
- @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
- might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
- If the usage of $className is not related to display specific configuration or UI, then there is
- technically no need to use the annotation, and you can annotate the class with
- @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
- """
+ "src/com/android/systemui/shade/example/ExampleClass.kt:$lineNumber: Error: UI elements of " +
+ "the shade window should use ShadeDisplayAware-annotated $className, as the shade " +
+ "might move between windows, and only @ShadeDisplayAware resources are updated with " +
+ "the new configuration correctly. Failures to do so might result in wrong dimensions " +
+ "for shade window classes (e.g. using the wrong density or theme). If the usage of " +
+ "$className is not related to display specific configuration or UI, then there is " +
+ "technically no need to use the annotation, and you can annotate the class with " +
+ "@SuppressLint(\"ShadeDisplayAwareContextChecker\")" +
+ "/@Suppress(\"ShadeDisplayAwareContextChecker\")"
}
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
new file mode 100644
index 000000000000..029b9cde4da9
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -0,0 +1,533 @@
+/*
+ * 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.compose.gesture
+
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.horizontalDrag
+import androidx.compose.foundation.gestures.verticalDrag
+import androidx.compose.foundation.overscroll
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerId
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
+import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
+import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.input.pointer.util.addPointerInputChange
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastSumBy
+import com.android.compose.modifiers.thenIf
+import kotlin.math.sign
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.launch
+
+/**
+ * A draggable that plays nicely with the nested scroll mechanism.
+ *
+ * This can be used whenever you need a draggable inside a scrollable or a draggable that contains a
+ * scrollable.
+ */
+interface NestedDraggable {
+ /**
+ * Called when a drag is started in the given [position] (*before* dragging the touch slop) and
+ * in the direction given by [sign], with the given number of [pointersDown] when the touch slop
+ * was detected.
+ */
+ fun onDragStarted(position: Offset, sign: Float, pointersDown: Int): Controller
+
+ /**
+ * Whether this draggable should consume any scroll amount with the given [sign] coming from a
+ * nested scrollable.
+ *
+ * This is called whenever a nested scrollable does not consume some scroll amount. If this
+ * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+ * consume all future events during preScroll until the nested scroll is finished.
+ */
+ fun shouldConsumeNestedScroll(sign: Float): Boolean
+
+ interface Controller {
+ /**
+ * Drag by [delta] pixels.
+ *
+ * @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
+ * nested scroll connection to be consumed by any composable above in the hierarchy. If
+ * the drag was performed on this draggable directly (instead of on a nested scrollable),
+ * any remaining delta will be used to overscroll this draggable.
+ */
+ fun onDrag(delta: Float): Float
+
+ /**
+ * Stop the current drag with the given [velocity].
+ *
+ * @return the consumed [velocity]. Any non-consumed velocity will be dispatched to the next
+ * nested scroll connection to be consumed by any composable above in the hierarchy. If
+ * the drag was performed on this draggable directly (instead of on a nested scrollable),
+ * any remaining velocity will be used to animate the overscroll of this draggable.
+ */
+ suspend fun onDragStopped(velocity: Float): Float
+ }
+}
+
+/**
+ * A draggable that supports nested scrolling and overscroll effects.
+ *
+ * @see NestedDraggable
+ */
+fun Modifier.nestedDraggable(
+ draggable: NestedDraggable,
+ orientation: Orientation,
+ overscrollEffect: OverscrollEffect? = null,
+ enabled: Boolean = true,
+): Modifier {
+ return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) }
+ .then(NestedDraggableElement(draggable, orientation, overscrollEffect, enabled))
+}
+
+private data class NestedDraggableElement(
+ private val draggable: NestedDraggable,
+ private val orientation: Orientation,
+ private val overscrollEffect: OverscrollEffect?,
+ private val enabled: Boolean,
+) : ModifierNodeElement<NestedDraggableNode>() {
+ override fun create(): NestedDraggableNode {
+ return NestedDraggableNode(draggable, orientation, overscrollEffect, enabled)
+ }
+
+ override fun update(node: NestedDraggableNode) {
+ node.update(draggable, orientation, overscrollEffect, enabled)
+ }
+}
+
+private class NestedDraggableNode(
+ private var draggable: NestedDraggable,
+ override var orientation: Orientation,
+ private var overscrollEffect: OverscrollEffect?,
+ private var enabled: Boolean,
+) :
+ DelegatingNode(),
+ PointerInputModifierNode,
+ NestedScrollConnection,
+ CompositionLocalConsumerModifierNode,
+ OrientationAware {
+ private val nestedScrollDispatcher = NestedScrollDispatcher()
+ private var trackDownPositionDelegate: SuspendingPointerInputModifierNode? = null
+ set(value) {
+ field?.let { undelegate(it) }
+ field = value?.also { delegate(it) }
+ }
+
+ private var detectDragsDelegate: SuspendingPointerInputModifierNode? = null
+ set(value) {
+ field?.let { undelegate(it) }
+ field = value?.also { delegate(it) }
+ }
+
+ /** The controller created by the nested scroll logic (and *not* the drag logic). */
+ private var nestedScrollController: WrappedController? = null
+ set(value) {
+ field?.ensureOnDragStoppedIsCalled()
+ field = value
+ }
+
+ /**
+ * The last pointer which was the first down since the last time all pointers were up.
+ *
+ * This is use to track the started position of a drag started on a nested scrollable.
+ */
+ private var lastFirstDown: Offset? = null
+
+ /** The number of pointers down. */
+ private var pointersDownCount = 0
+
+ init {
+ delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
+ }
+
+ override fun onDetach() {
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+ }
+
+ fun update(
+ draggable: NestedDraggable,
+ orientation: Orientation,
+ overscrollEffect: OverscrollEffect?,
+ enabled: Boolean,
+ ) {
+ this.draggable = draggable
+ this.orientation = orientation
+ this.overscrollEffect = overscrollEffect
+ this.enabled = enabled
+
+ trackDownPositionDelegate?.resetPointerInputHandler()
+ detectDragsDelegate?.resetPointerInputHandler()
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+
+ if (!enabled && trackDownPositionDelegate != null) {
+ check(detectDragsDelegate != null)
+ trackDownPositionDelegate = null
+ detectDragsDelegate = null
+ }
+ }
+
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize,
+ ) {
+ if (!enabled) return
+
+ if (trackDownPositionDelegate == null) {
+ check(detectDragsDelegate == null)
+ trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() }
+ detectDragsDelegate = SuspendingPointerInputModifierNode { detectDrags() }
+ }
+
+ checkNotNull(trackDownPositionDelegate).onPointerEvent(pointerEvent, pass, bounds)
+ checkNotNull(detectDragsDelegate).onPointerEvent(pointerEvent, pass, bounds)
+ }
+
+ override fun onCancelPointerInput() {
+ trackDownPositionDelegate?.onCancelPointerInput()
+ detectDragsDelegate?.onCancelPointerInput()
+ }
+
+ /*
+ * ======================================
+ * ===== Pointer input (drag) logic =====
+ * ======================================
+ */
+
+ private suspend fun PointerInputScope.detectDrags() {
+ // Lazily create the velocity tracker when the pointer input restarts.
+ val velocityTracker = VelocityTracker()
+
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ check(down.position == lastFirstDown) {
+ "Position from detectDrags() is not the same as position in trackDownPosition()"
+ }
+ check(pointersDownCount == 1) { "pointersDownCount is equal to $pointersDownCount" }
+
+ var overSlop = 0f
+ val onTouchSlopReached = { change: PointerInputChange, over: Float ->
+ change.consume()
+ overSlop = over
+ }
+
+ suspend fun AwaitPointerEventScope.awaitTouchSlopOrCancellation(
+ pointerId: PointerId
+ ): PointerInputChange? {
+ return when (orientation) {
+ Orientation.Horizontal ->
+ awaitHorizontalTouchSlopOrCancellation(pointerId, onTouchSlopReached)
+ Orientation.Vertical ->
+ awaitVerticalTouchSlopOrCancellation(pointerId, onTouchSlopReached)
+ }
+ }
+
+ var drag = awaitTouchSlopOrCancellation(down.id)
+
+ // We try to pick-up the drag gesture in case the touch slop swipe was consumed by a
+ // nested scrollable child that disappeared.
+ // This was copied from http://shortn/_10L8U02IoL.
+ // TODO(b/380838584): Reuse detect(Horizontal|Vertical)DragGestures() instead.
+ while (drag == null && currentEvent.changes.fastAny { it.pressed }) {
+ var event: PointerEvent
+ do {
+ event = awaitPointerEvent()
+ } while (
+ event.changes.fastAny { it.isConsumed } && event.changes.fastAny { it.pressed }
+ )
+
+ // An event was not consumed and there's still a pointer in the screen.
+ if (event.changes.fastAny { it.pressed }) {
+ // Await touch slop again, using the initial down as starting point.
+ // For most cases this should return immediately since we probably moved
+ // far enough from the initial down event.
+ drag = awaitTouchSlopOrCancellation(down.id)
+ }
+ }
+
+ if (drag != null) {
+ velocityTracker.resetTracking()
+ val sign = (drag.position - down.position).toFloat().sign
+ check(pointersDownCount > 0) { "pointersDownCount is equal to $pointersDownCount" }
+ val wrappedController =
+ WrappedController(
+ coroutineScope,
+ draggable.onDragStarted(down.position, sign, pointersDownCount),
+ )
+ if (overSlop != 0f) {
+ onDrag(wrappedController, drag, overSlop, velocityTracker)
+ }
+
+ // If a drag was started, we cancel any other drag started by a nested scrollable.
+ //
+ // Note: we cancel the nested drag here *after* starting the new drag so that in the
+ // STL case, the cancelled drag will not change the current scene of the STL.
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+
+ val isSuccessful =
+ try {
+ val onDrag = { change: PointerInputChange ->
+ onDrag(
+ wrappedController,
+ change,
+ change.positionChange().toFloat(),
+ velocityTracker,
+ )
+ change.consume()
+ }
+
+ when (orientation) {
+ Orientation.Horizontal -> horizontalDrag(drag.id, onDrag)
+ Orientation.Vertical -> verticalDrag(drag.id, onDrag)
+ }
+ } catch (t: Throwable) {
+ wrappedController.ensureOnDragStoppedIsCalled()
+ throw t
+ }
+
+ if (isSuccessful) {
+ val maxVelocity = currentValueOf(LocalViewConfiguration).maximumFlingVelocity
+ val velocity =
+ velocityTracker
+ .calculateVelocity(Velocity(maxVelocity, maxVelocity))
+ .toFloat()
+ onDragStopped(wrappedController, velocity)
+ } else {
+ onDragStopped(wrappedController, velocity = 0f)
+ }
+ }
+ }
+ }
+
+ private fun onDrag(
+ controller: NestedDraggable.Controller,
+ change: PointerInputChange,
+ delta: Float,
+ velocityTracker: VelocityTracker,
+ ) {
+ velocityTracker.addPointerInputChange(change)
+
+ scrollWithOverscroll(delta) { deltaFromOverscroll ->
+ scrollWithNestedScroll(deltaFromOverscroll) { deltaFromNestedScroll ->
+ controller.onDrag(deltaFromNestedScroll)
+ }
+ }
+ }
+
+ private fun onDragStopped(controller: WrappedController, velocity: Float) {
+ coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ try {
+ flingWithOverscroll(velocity) { velocityFromOverscroll ->
+ flingWithNestedScroll(velocityFromOverscroll) { velocityFromNestedScroll ->
+ controller.onDragStopped(velocityFromNestedScroll)
+ }
+ }
+ } finally {
+ controller.ensureOnDragStoppedIsCalled()
+ }
+ }
+ }
+
+ private fun scrollWithOverscroll(delta: Float, performScroll: (Float) -> Float): Float {
+ val effect = overscrollEffect
+ return if (effect != null) {
+ effect
+ .applyToScroll(delta.toOffset(), source = NestedScrollSource.UserInput) {
+ performScroll(it.toFloat()).toOffset()
+ }
+ .toFloat()
+ } else {
+ performScroll(delta)
+ }
+ }
+
+ private fun scrollWithNestedScroll(delta: Float, performScroll: (Float) -> Float): Float {
+ val preConsumed =
+ nestedScrollDispatcher
+ .dispatchPreScroll(
+ available = delta.toOffset(),
+ source = NestedScrollSource.UserInput,
+ )
+ .toFloat()
+ val available = delta - preConsumed
+ val consumed = performScroll(available)
+ val left = available - consumed
+ val postConsumed =
+ nestedScrollDispatcher
+ .dispatchPostScroll(
+ consumed = (preConsumed + consumed).toOffset(),
+ available = left.toOffset(),
+ source = NestedScrollSource.UserInput,
+ )
+ .toFloat()
+ return consumed + preConsumed + postConsumed
+ }
+
+ private suspend fun flingWithOverscroll(
+ velocity: Float,
+ performFling: suspend (Float) -> Float,
+ ) {
+ val effect = overscrollEffect
+ if (effect != null) {
+ effect.applyToFling(velocity.toVelocity()) { performFling(it.toFloat()).toVelocity() }
+ } else {
+ performFling(velocity)
+ }
+ }
+
+ private suspend fun flingWithNestedScroll(
+ velocity: Float,
+ performFling: suspend (Float) -> Float,
+ ): Float {
+ val preConsumed = nestedScrollDispatcher.dispatchPreFling(available = velocity.toVelocity())
+ val available = velocity - preConsumed.toFloat()
+ val consumed = performFling(available)
+ val left = available - consumed
+ return nestedScrollDispatcher
+ .dispatchPostFling(
+ consumed = consumed.toVelocity() + preConsumed,
+ available = left.toVelocity(),
+ )
+ .toFloat()
+ }
+
+ /*
+ * ===============================
+ * ===== Nested scroll logic =====
+ * ===============================
+ */
+
+ private suspend fun PointerInputScope.trackDownPosition() {
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ lastFirstDown = down.position
+ pointersDownCount = 1
+
+ do {
+ pointersDownCount +=
+ awaitPointerEvent().changes.fastSumBy { change ->
+ when {
+ change.changedToDownIgnoreConsumed() -> 1
+ change.changedToUpIgnoreConsumed() -> -1
+ else -> 0
+ }
+ }
+ } while (pointersDownCount > 0)
+ }
+ }
+
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val controller = nestedScrollController ?: return Offset.Zero
+ val consumed = controller.onDrag(available.toFloat())
+ return consumed.toOffset()
+ }
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset {
+ if (source == NestedScrollSource.SideEffect) {
+ check(nestedScrollController == null)
+ return Offset.Zero
+ }
+
+ val offset = available.toFloat()
+ if (offset == 0f) {
+ return Offset.Zero
+ }
+
+ val sign = offset.sign
+ if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) {
+ val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }
+
+ // TODO(b/382665591): Replace this by check(pointersDownCount > 0).
+ val pointersDown = pointersDownCount.coerceAtLeast(1)
+ nestedScrollController =
+ WrappedController(
+ coroutineScope,
+ draggable.onDragStarted(startedPosition, sign, pointersDown),
+ )
+ }
+
+ val controller = nestedScrollController ?: return Offset.Zero
+ return controller.onDrag(offset).toOffset()
+ }
+
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ val controller = nestedScrollController ?: return Velocity.Zero
+ nestedScrollController = null
+
+ val consumed = controller.onDragStopped(available.toFloat())
+ return consumed.toVelocity()
+ }
+}
+
+/**
+ * A controller that wraps [delegate] and can be used to ensure that [onDragStopped] is called, but
+ * not more than once.
+ */
+private class WrappedController(
+ private val coroutineScope: CoroutineScope,
+ private val delegate: NestedDraggable.Controller,
+) : NestedDraggable.Controller by delegate {
+ private var onDragStoppedCalled = false
+
+ override fun onDrag(delta: Float): Float {
+ if (onDragStoppedCalled) return 0f
+ return delegate.onDrag(delta)
+ }
+
+ override suspend fun onDragStopped(velocity: Float): Float {
+ if (onDragStoppedCalled) return 0f
+ onDragStoppedCalled = true
+ return delegate.onDragStopped(velocity)
+ }
+
+ fun ensureOnDragStoppedIsCalled() {
+ // Start with UNDISPATCHED so that onDragStopped() is always run until its first suspension
+ // point, even if coroutineScope is cancelled.
+ coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { onDragStopped(velocity = 0f) }
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt
new file mode 100644
index 000000000000..6e91727e68e3
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.gesture
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Velocity
+
+/**
+ * An interface to conveniently convert a [Float] to and from an [Offset] or a [Velocity] given an
+ * [orientation].
+ */
+interface OrientationAware {
+ val orientation: Orientation
+
+ fun Float.toOffset(): Offset {
+ return when (orientation) {
+ Orientation.Horizontal -> Offset(x = this, y = 0f)
+ Orientation.Vertical -> Offset(x = 0f, y = this)
+ }
+ }
+
+ fun Float.toVelocity(): Velocity {
+ return when (orientation) {
+ Orientation.Horizontal -> Velocity(x = this, y = 0f)
+ Orientation.Vertical -> Velocity(x = 0f, y = this)
+ }
+ }
+
+ fun Offset.toFloat(): Float {
+ return when (orientation) {
+ Orientation.Horizontal -> this.x
+ Orientation.Vertical -> this.y
+ }
+ }
+
+ fun Velocity.toFloat(): Float {
+ return when (orientation) {
+ Orientation.Horizontal -> this.x
+ Orientation.Vertical -> this.y
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
index a499447fc367..fe3faafded6f 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
@@ -37,4 +37,4 @@ fun colorAttr(context: Context, @AttrRes attr: Int): Color {
@ColorInt val color = ta.getColor(0, 0)
ta.recycle()
return Color(color)
-}
+} \ No newline at end of file
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
new file mode 100644
index 000000000000..735ab68bc6a6
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -0,0 +1,523 @@
+/*
+ * 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.compose.gesture
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+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.nestedScroll
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeDown
+import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.unit.Velocity
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.ceil
+import kotlinx.coroutines.awaitCancellation
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class NestedDraggableTest(override val orientation: Orientation) : OrientationAware {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun orientations() = listOf(Orientation.Horizontal, Orientation.Vertical)
+ }
+
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun simpleDrag() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ var rootCenter = Offset.Zero
+ rule.onRoot().performTouchInput {
+ rootCenter = center
+ down(center)
+ moveBy((touchSlop + 10f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(10f)
+ assertThat(draggable.onDragStartedPosition).isEqualTo(rootCenter)
+ assertThat(draggable.onDragStartedSign).isEqualTo(1f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy(20f.toOffset()) }
+
+ assertThat(draggable.onDragDelta).isEqualTo(30f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ moveBy((-15f).toOffset())
+ up()
+ }
+
+ assertThat(draggable.onDragDelta).isEqualTo(15f)
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun nestedScrollable() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ var rootCenter = Offset.Zero
+ rule.onRoot().performTouchInput {
+ rootCenter = center
+ down(center)
+ moveBy((-touchSlop - 10f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-10f)
+ assertThat(draggable.onDragStartedPosition).isEqualTo(rootCenter)
+ assertThat(draggable.onDragStartedSign).isEqualTo(-1f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy((-20f).toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-30f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ moveBy(15f.toOffset())
+ up()
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-15f)
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() {
+ val draggable = TestDraggable()
+ var orientation by mutableStateOf(orientation)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ orientation =
+ when (orientation) {
+ Orientation.Horizontal -> Orientation.Vertical
+ Orientation.Vertical -> Orientation.Horizontal
+ }
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset_nestedScroll() {
+ val draggable = TestDraggable()
+ var orientation by mutableStateOf(orientation)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ orientation =
+ when (orientation) {
+ Orientation.Horizontal -> Orientation.Vertical
+ Orientation.Vertical -> Orientation.Horizontal
+ }
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringDrag() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ if (composeContent) {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringDrag_nestedScroll() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ if (composeContent) {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringFling() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ var preFlingCalled = false
+ rule.setContent {
+ if (composeContent) {
+ Box(
+ Modifier.fillMaxSize()
+ // This nested scroll connection indefinitely suspends on pre fling, so that
+ // we can emulate what happens when the draggable is removed from
+ // composition while the pre-fling happens and onDragStopped() was not
+ // called yet.
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ preFlingCalled = true
+ awaitCancellation()
+ }
+ }
+ }
+ )
+ .nestedDraggable(draggable, orientation)
+ )
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ // Swipe down.
+ rule.onRoot().performTouchInput {
+ when (orientation) {
+ Orientation.Horizontal -> swipeLeft()
+ Orientation.Vertical -> swipeDown()
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+ assertThat(preFlingCalled).isTrue()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ @Ignore("b/303224944#comment22")
+ fun onDragStoppedIsCalledWhenNestedScrollableIsRemoved() {
+ val draggable = TestDraggable()
+ var composeNestedScrollable by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .then(
+ if (composeNestedScrollable) {
+ Modifier.nestedScrollable(rememberScrollState())
+ } else {
+ Modifier
+ }
+ )
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeNestedScrollable = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun enabled() {
+ val draggable = TestDraggable()
+ var enabled by mutableStateOf(false)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation, enabled = enabled)
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ enabled = true
+ rule.onRoot().performTouchInput {
+ // Release previously up finger.
+ up()
+
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ enabled = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun pointersDown() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ (1..5).forEach { nDown ->
+ rule.onRoot().performTouchInput {
+ repeat(nDown) { pointerId -> down(pointerId, center) }
+
+ moveBy(pointerId = 0, touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedPointersDown).isEqualTo(nDown)
+
+ rule.onRoot().performTouchInput {
+ repeat(nDown) { pointerId -> up(pointerId = pointerId) }
+ }
+ }
+ }
+
+ @Test
+ fun pointersDown_nestedScroll() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+
+ (1..5).forEach { nDown ->
+ rule.onRoot().performTouchInput {
+ repeat(nDown) { pointerId -> down(pointerId, center) }
+
+ moveBy(pointerId = 0, (touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedPointersDown).isEqualTo(nDown)
+
+ rule.onRoot().performTouchInput {
+ repeat(nDown) { pointerId -> up(pointerId = pointerId) }
+ }
+ }
+ }
+
+ @Test
+ fun pointersDown_downThenUpThenDown() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ val slopThird = ceil(touchSlop / 3f).toOffset()
+ rule.onRoot().performTouchInput {
+ repeat(5) { down(pointerId = it, center) } // + 5
+ moveBy(pointerId = 0, slopThird)
+
+ listOf(2, 3).forEach { up(pointerId = it) } // - 2
+ moveBy(pointerId = 0, slopThird)
+
+ listOf(5, 6, 7).forEach { down(pointerId = it, center) } // + 3
+ moveBy(pointerId = 0, slopThird)
+ }
+
+ assertThat(draggable.onDragStartedPointersDown).isEqualTo(6)
+ }
+
+ private fun ComposeContentTestRule.setContentWithTouchSlop(
+ content: @Composable () -> Unit
+ ): Float {
+ var touchSlop = 0f
+ setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ content()
+ }
+ return touchSlop
+ }
+
+ private fun Modifier.nestedScrollable(scrollState: ScrollState): Modifier {
+ return when (orientation) {
+ Orientation.Vertical -> verticalScroll(scrollState)
+ Orientation.Horizontal -> horizontalScroll(scrollState)
+ }
+ }
+
+ private class TestDraggable(
+ private val onDragStarted: (Offset, Float) -> Unit = { _, _ -> },
+ private val onDrag: (Float) -> Float = { it },
+ private val onDragStopped: suspend (Float) -> Float = { it },
+ private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+ ) : NestedDraggable {
+ var onDragStartedCalled = false
+ var onDragCalled = false
+ var onDragStoppedCalled = false
+
+ var onDragStartedPosition = Offset.Zero
+ var onDragStartedSign = 0f
+ var onDragStartedPointersDown = 0
+ var onDragDelta = 0f
+
+ override fun onDragStarted(
+ position: Offset,
+ sign: Float,
+ pointersDown: Int,
+ ): NestedDraggable.Controller {
+ onDragStartedCalled = true
+ onDragStartedPosition = position
+ onDragStartedSign = sign
+ onDragStartedPointersDown = pointersDown
+ onDragDelta = 0f
+
+ onDragStarted.invoke(position, sign)
+ return object : NestedDraggable.Controller {
+ override fun onDrag(delta: Float): Float {
+ onDragCalled = true
+ onDragDelta += delta
+ return onDrag.invoke(delta)
+ }
+
+ override suspend fun onDragStopped(velocity: Float): Float {
+ onDragStoppedCalled = true
+ return onDragStopped.invoke(velocity)
+ }
+ }
+ }
+
+ override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ return shouldConsumeNestedScroll.invoke(sign)
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
index 737853b88f7a..96989a2df2f0 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
@@ -16,8 +16,8 @@
package com.android.compose.theme
+import android.annotation.ColorRes
import android.content.Context
-import androidx.annotation.AttrRes
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -70,118 +70,118 @@ class PlatformThemeTest {
val colorValues = mutableListOf<ColorValue>()
fun onLaunch(colorScheme: ColorScheme, context: Context) {
- fun addValue(name: String, materialValue: Color, @AttrRes attr: Int) {
- colorValues.add(ColorValue(name, materialValue, colorAttr(context, attr)))
+ fun addValue(name: String, materialValue: Color, @ColorRes color: Int) {
+ colorValues.add(ColorValue(name, materialValue, Color(context.getColor(color))))
}
- addValue("primary", colorScheme.primary, R.attr.materialColorPrimary)
- addValue("onPrimary", colorScheme.onPrimary, R.attr.materialColorOnPrimary)
+ addValue("primary", colorScheme.primary, R.color.materialColorPrimary)
+ addValue("onPrimary", colorScheme.onPrimary, R.color.materialColorOnPrimary)
addValue(
"primaryContainer",
colorScheme.primaryContainer,
- R.attr.materialColorPrimaryContainer,
+ R.color.materialColorPrimaryContainer,
)
addValue(
"onPrimaryContainer",
colorScheme.onPrimaryContainer,
- R.attr.materialColorOnPrimaryContainer,
+ R.color.materialColorOnPrimaryContainer,
)
addValue(
"inversePrimary",
colorScheme.inversePrimary,
- R.attr.materialColorInversePrimary,
+ R.color.materialColorInversePrimary,
)
- addValue("secondary", colorScheme.secondary, R.attr.materialColorSecondary)
- addValue("onSecondary", colorScheme.onSecondary, R.attr.materialColorOnSecondary)
+ addValue("secondary", colorScheme.secondary, R.color.materialColorSecondary)
+ addValue("onSecondary", colorScheme.onSecondary, R.color.materialColorOnSecondary)
addValue(
"secondaryContainer",
colorScheme.secondaryContainer,
- R.attr.materialColorSecondaryContainer,
+ R.color.materialColorSecondaryContainer,
)
addValue(
"onSecondaryContainer",
colorScheme.onSecondaryContainer,
- R.attr.materialColorOnSecondaryContainer,
+ R.color.materialColorOnSecondaryContainer,
)
- addValue("tertiary", colorScheme.tertiary, R.attr.materialColorTertiary)
- addValue("onTertiary", colorScheme.onTertiary, R.attr.materialColorOnTertiary)
+ addValue("tertiary", colorScheme.tertiary, R.color.materialColorTertiary)
+ addValue("onTertiary", colorScheme.onTertiary, R.color.materialColorOnTertiary)
addValue(
"tertiaryContainer",
colorScheme.tertiaryContainer,
- R.attr.materialColorTertiaryContainer,
+ R.color.materialColorTertiaryContainer,
)
addValue(
"onTertiaryContainer",
colorScheme.onTertiaryContainer,
- R.attr.materialColorOnTertiaryContainer,
+ R.color.materialColorOnTertiaryContainer,
)
- addValue("onBackground", colorScheme.onBackground, R.attr.materialColorOnBackground)
- addValue("surface", colorScheme.surface, R.attr.materialColorSurface)
- addValue("onSurface", colorScheme.onSurface, R.attr.materialColorOnSurface)
+ addValue("onBackground", colorScheme.onBackground, R.color.materialColorOnBackground)
+ addValue("surface", colorScheme.surface, R.color.materialColorSurface)
+ addValue("onSurface", colorScheme.onSurface, R.color.materialColorOnSurface)
addValue(
"surfaceVariant",
colorScheme.surfaceVariant,
- R.attr.materialColorSurfaceVariant,
+ R.color.materialColorSurfaceVariant,
)
addValue(
"onSurfaceVariant",
colorScheme.onSurfaceVariant,
- R.attr.materialColorOnSurfaceVariant,
+ R.color.materialColorOnSurfaceVariant,
)
addValue(
"inverseSurface",
colorScheme.inverseSurface,
- R.attr.materialColorInverseSurface,
+ R.color.materialColorInverseSurface,
)
addValue(
"inverseOnSurface",
colorScheme.inverseOnSurface,
- R.attr.materialColorInverseOnSurface,
+ R.color.materialColorInverseOnSurface,
)
- addValue("error", colorScheme.error, R.attr.materialColorError)
- addValue("onError", colorScheme.onError, R.attr.materialColorOnError)
+ addValue("error", colorScheme.error, R.color.materialColorError)
+ addValue("onError", colorScheme.onError, R.color.materialColorOnError)
addValue(
"errorContainer",
colorScheme.errorContainer,
- R.attr.materialColorErrorContainer,
+ R.color.materialColorErrorContainer,
)
addValue(
"onErrorContainer",
colorScheme.onErrorContainer,
- R.attr.materialColorOnErrorContainer,
+ R.color.materialColorOnErrorContainer,
)
- addValue("outline", colorScheme.outline, R.attr.materialColorOutline)
+ addValue("outline", colorScheme.outline, R.color.materialColorOutline)
addValue(
"outlineVariant",
colorScheme.outlineVariant,
- R.attr.materialColorOutlineVariant,
+ R.color.materialColorOutlineVariant,
)
- addValue("surfaceBright", colorScheme.surfaceBright, R.attr.materialColorSurfaceBright)
- addValue("surfaceDim", colorScheme.surfaceDim, R.attr.materialColorSurfaceDim)
+ addValue("surfaceBright", colorScheme.surfaceBright, R.color.materialColorSurfaceBright)
+ addValue("surfaceDim", colorScheme.surfaceDim, R.color.materialColorSurfaceDim)
addValue(
"surfaceContainer",
colorScheme.surfaceContainer,
- R.attr.materialColorSurfaceContainer,
+ R.color.materialColorSurfaceContainer,
)
addValue(
"surfaceContainerHigh",
colorScheme.surfaceContainerHigh,
- R.attr.materialColorSurfaceContainerHigh,
+ R.color.materialColorSurfaceContainerHigh,
)
addValue(
"surfaceContainerHighest",
colorScheme.surfaceContainerHighest,
- R.attr.materialColorSurfaceContainerHighest,
+ R.color.materialColorSurfaceContainerHighest,
)
addValue(
"surfaceContainerLow",
colorScheme.surfaceContainerLow,
- R.attr.materialColorSurfaceContainerLow,
+ R.color.materialColorSurfaceContainerLow,
)
addValue(
"surfaceContainerLowest",
colorScheme.surfaceContainerLowest,
- R.attr.materialColorSurfaceContainerLowest,
+ R.color.materialColorSurfaceContainerLowest,
)
}
@@ -200,9 +200,9 @@ class PlatformThemeTest {
"MaterialTheme.colorScheme.${colorValue.name} matches attribute color"
)
.that(colorValue.materialValue)
- .isEqualTo(colorValue.attrValue)
+ .isEqualTo(colorValue.colorValue)
}
}
- private data class ColorValue(val name: String, val materialValue: Color, val attrValue: Color)
+ private data class ColorValue(val name: String, val materialValue: Color, val colorValue: Color)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 85f549d43a11..55b42931b1fa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.composable
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.overscroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -101,6 +102,7 @@ private fun SceneScope.BouncerScene(
viewModel,
dialogFactory,
Modifier.element(Bouncer.Elements.Content)
+ .overscroll(verticalOverscrollEffect)
.sysuiResTag(Bouncer.TestTags.Root)
.fillMaxSize(),
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Color.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Color.kt
index 64b9f2df144b..81ae6b39e06b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Color.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Color.kt
@@ -19,6 +19,7 @@ package com.android.systemui.common.ui.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
import com.android.compose.theme.colorAttr
/** Resolves [com.android.systemui.common.shared.model.Color] into [Color] */
@@ -28,5 +29,6 @@ fun com.android.systemui.common.shared.model.Color.toColor(): Color {
return when (this) {
is com.android.systemui.common.shared.model.Color.Attribute -> colorAttr(attribute)
is com.android.systemui.common.shared.model.Color.Loaded -> Color(color)
+ is com.android.systemui.common.shared.model.Color.Resource -> colorResource(colorRes)
}
}
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 4705d8dd86c4..beaf9631ae5a 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
@@ -44,7 +44,6 @@ import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -188,7 +187,7 @@ fun CommunalContainer(
scene(
CommunalScenes.Blank,
userActions =
- if (communalHubOnMobile()) emptyMap()
+ if (viewModel.v2FlagEnabled()) emptyMap()
else mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal),
) {
// This scene shows nothing only allowing for transitions to the communal scene.
@@ -198,7 +197,8 @@ fun CommunalContainer(
scene(
CommunalScenes.Communal,
userActions =
- if (communalHubOnMobile()) emptyMap() else mapOf(Swipe.End to CommunalScenes.Blank),
+ if (viewModel.v2FlagEnabled()) emptyMap()
+ else mapOf(Swipe.End to CommunalScenes.Blank),
) {
CommunalScene(
backgroundType = backgroundType,
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 778d7e7f99b3..a17a1d46554f 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
@@ -23,11 +23,17 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
+import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
@@ -48,6 +54,7 @@ constructor(
private val ambientStatusBarSection: AmbientStatusBarSection,
private val communalPopupSection: CommunalPopupSection,
private val widgetSection: CommunalAppWidgetSection,
+ private val communalToDreamButtonSection: CommunalToDreamButtonSection,
) {
@Composable
@@ -59,7 +66,7 @@ constructor(
Box(modifier = Modifier.fillMaxSize()) {
with(communalPopupSection) { Popup() }
with(ambientStatusBarSection) {
- AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
}
CommunalHub(
viewModel = viewModel,
@@ -81,11 +88,13 @@ constructor(
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
+ with(communalToDreamButtonSection) { Button() }
},
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
+ val screensaverButtonMeasurable: Measurable? = measurables.getOrNull(3)
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -100,6 +109,15 @@ constructor(
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
+ val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
+ val screensaverButtonPlaceable =
+ screensaverButtonMeasurable?.measure(
+ Constraints.fixed(
+ width = screensaverButtonSizeInt,
+ height = screensaverButtonSizeInt,
+ )
+ )
+
val communalGridPlaceable =
communalGridMeasurable.measure(
noMinConstraints.copy(maxHeight = lockIconBounds.top)
@@ -108,12 +126,22 @@ constructor(
layout(constraints.maxWidth, constraints.maxHeight) {
communalGridPlaceable.place(x = 0, y = 0)
lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
- bottomAreaPlaceable.place(
- x = 0,
- y = constraints.maxHeight - bottomAreaPlaceable.height,
+
+ val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
+ bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
+ screensaverButtonPlaceable?.place(
+ x =
+ constraints.maxWidth -
+ screensaverButtonSizeInt -
+ Dimensions.ItemSpacing.roundToPx(),
+ y = lockIconBounds.top,
)
}
}
}
}
+
+ companion object {
+ val screensaverButtonSize: Dp = 64.dp
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 573e5ca5e2d5..5dbedc7045e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -43,6 +43,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
+import androidx.compose.foundation.gestures.snapping.SnapPosition
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -151,6 +153,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
@@ -187,6 +190,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
+import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.lifecycle.rememberViewModel
@@ -217,6 +221,7 @@ fun CommunalHub(
var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var toolbarSize: IntSize? by remember { mutableStateOf(null) }
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
+ var contentOffset: Offset by remember { mutableStateOf(Offset.Zero) }
val gridState =
rememberLazyGridState(viewModel.savedFirstScrollIndex, viewModel.savedFirstScrollOffset)
@@ -239,9 +244,7 @@ fun CommunalHub(
initialValue = !viewModel.isEditMode
)
- val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
- val contentOffset = beforeContentPadding(contentPadding).toOffset()
-
+ val minContentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
ObserveScrollEffect(gridState, viewModel)
val context = LocalContext.current
@@ -293,9 +296,19 @@ fun CommunalHub(
offset.y,
) - contentOffset
val index = firstIndexAtOffset(gridState, adjustedOffset)
- val key =
+ val tappedKey =
index?.let { keyAtIndexIfEditable(contentListState.list, index) }
- viewModel.setSelectedKey(key)
+
+ viewModel.setSelectedKey(
+ if (
+ Flags.hubEditModeTouchAdjustments() &&
+ selectedKey.value == tappedKey
+ ) {
+ null
+ } else {
+ tappedKey
+ }
+ )
}
}
}
@@ -367,7 +380,7 @@ fun CommunalHub(
) {
AccessibilityContainer(viewModel) {
if (!viewModel.isEditMode && isEmptyState) {
- EmptyStateCta(contentPadding = contentPadding, viewModel = viewModel)
+ EmptyStateCta(contentPadding = minContentPadding, viewModel = viewModel)
} else {
val slideOffsetInPx =
with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() }
@@ -396,10 +409,11 @@ fun CommunalHub(
CommunalHubLazyGrid(
communalContent = communalContent,
viewModel = viewModel,
- contentPadding = contentPadding,
+ minContentPadding = minContentPadding,
contentOffset = contentOffset,
screenWidth = screenWidth,
setGridCoordinates = { gridCoordinates = it },
+ setContentOffset = { contentOffset = it },
updateDragPositionForRemove = { boundingBox ->
val gridOffset = gridCoordinates?.positionInWindow()
val removeButtonCenter =
@@ -741,27 +755,40 @@ fun calculateWidgetSize(
@Composable
private fun HorizontalGridWrapper(
- contentPadding: PaddingValues,
+ minContentPadding: PaddingValues,
gridState: LazyGridState,
+ setContentOffset: (offset: Offset) -> Unit,
modifier: Modifier = Modifier,
content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit,
) {
if (communalResponsiveGrid()) {
+ val flingBehavior =
+ rememberSnapFlingBehavior(lazyGridState = gridState, snapPosition = SnapPosition.Start)
ResponsiveLazyHorizontalGrid(
cellAspectRatio = 1.5f,
modifier = modifier,
state = gridState,
- minContentPadding = contentPadding,
+ flingBehavior = flingBehavior,
+ minContentPadding = minContentPadding,
minHorizontalArrangement = Dimensions.ItemSpacing,
minVerticalArrangement = Dimensions.ItemSpacing,
+ setContentOffset = setContentOffset,
content = content,
)
} else {
+ val layoutDirection = LocalLayoutDirection.current
+ val density = LocalDensity.current
+
+ val minStartPadding = minContentPadding.calculateStartPadding(layoutDirection)
+ val minTopPadding = minContentPadding.calculateTopPadding()
+
+ with(density) { setContentOffset(Offset(minStartPadding.toPx(), minTopPadding.toPx())) }
+
LazyHorizontalGrid(
modifier = modifier,
state = gridState,
rows = GridCells.Fixed(CommunalContentSize.FixedSize.FULL.span),
- contentPadding = contentPadding,
+ contentPadding = minContentPadding,
horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
) {
@@ -775,13 +802,14 @@ private fun HorizontalGridWrapper(
private fun BoxScope.CommunalHubLazyGrid(
communalContent: List<CommunalContentModel>,
viewModel: BaseCommunalViewModel,
- contentPadding: PaddingValues,
+ minContentPadding: PaddingValues,
selectedKey: State<String?>,
screenWidth: Int,
contentOffset: Offset,
gridState: LazyGridState,
contentListState: ContentListState,
setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
+ setContentOffset: (offset: Offset) -> Unit,
updateDragPositionForRemove: (boundingBox: IntRect) -> Boolean,
widgetConfigurator: WidgetConfigurator?,
interactionHandler: RemoteViews.InteractionHandler?,
@@ -832,10 +860,19 @@ private fun BoxScope.CommunalHubLazyGrid(
HorizontalGridWrapper(
modifier = gridModifier,
gridState = gridState,
- contentPadding = contentPadding,
+ minContentPadding = minContentPadding,
+ setContentOffset = setContentOffset,
) { sizeInfo ->
+ /** Override spans based on the responsive grid size */
+ val finalizedList =
+ if (sizeInfo != null) {
+ resizeOngoingItems(list, sizeInfo.gridSize.height)
+ } else {
+ list
+ }
+
itemsIndexed(
- items = list,
+ items = finalizedList,
key = { _, item -> item.key },
contentType = { _, item -> item.key },
span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) },
@@ -883,12 +920,12 @@ private fun BoxScope.CommunalHubLazyGrid(
key = item.key,
currentSpan = GridItemSpan(currentItemSpan),
gridState = gridState,
- gridContentPadding = contentPadding,
+ gridContentPadding = sizeInfo?.contentPadding ?: minContentPadding,
verticalArrangement =
Arrangement.spacedBy(
sizeInfo?.verticalArrangement ?: Dimensions.ItemSpacing
),
- enabled = selected,
+ enabled = selected && !isItemDragging,
alpha = { outlineAlpha },
modifier =
Modifier.requiredSize(dpSize)
@@ -926,12 +963,28 @@ private fun BoxScope.CommunalHubLazyGrid(
}
}
} else {
+ val itemAlpha =
+ if (communalResponsiveGrid()) {
+ val percentVisible by
+ remember(gridState, index) {
+ derivedStateOf { calculatePercentVisible(gridState, index) }
+ }
+ animateFloatAsState(percentVisible)
+ } else {
+ null
+ }
+
CommunalContent(
model = item,
viewModel = viewModel,
size = size,
selected = false,
- modifier = Modifier.requiredSize(dpSize).animateItem(),
+ modifier =
+ Modifier.requiredSize(dpSize).animateItem().thenIf(
+ communalResponsiveGrid()
+ ) {
+ Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+ },
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
@@ -967,7 +1020,7 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal
text = titleForEmptyStateCTA,
style = MaterialTheme.typography.displaySmall,
textAlign = TextAlign.Center,
- color = colors.primary,
+ color = colors.onPrimary,
modifier =
Modifier.focusable().semantics(mergeDescendants = true) {
contentDescription = titleForEmptyStateCTA
@@ -1037,17 +1090,27 @@ private fun Toolbar(
.onSizeChanged { setToolbarSize(it) }
) {
val addWidgetText = stringResource(R.string.hub_mode_add_widget_button_text)
- ToolbarButton(
- isPrimary = !removeEnabled,
- modifier = Modifier.align(Alignment.CenterStart),
- onClick = onOpenWidgetPicker,
- ) {
- Icon(Icons.Default.Add, null)
- Text(text = addWidgetText)
+
+ if (!(Flags.hubEditModeTouchAdjustments() && removeEnabled)) {
+ ToolbarButton(
+ isPrimary = !removeEnabled,
+ modifier = Modifier.align(Alignment.CenterStart),
+ onClick = onOpenWidgetPicker,
+ ) {
+ Icon(Icons.Default.Add, null)
+ Text(text = addWidgetText)
+ }
}
AnimatedVisibility(
- modifier = Modifier.align(Alignment.Center),
+ modifier =
+ Modifier.align(
+ if (Flags.hubEditModeTouchAdjustments()) {
+ Alignment.CenterStart
+ } else {
+ Alignment.Center
+ }
+ ),
visible = removeEnabled,
enter = fadeIn(),
exit = fadeOut(),
@@ -1070,7 +1133,11 @@ private fun Toolbar(
horizontalArrangement =
Arrangement.spacedBy(
ButtonDefaults.IconSpacing,
- Alignment.CenterHorizontally,
+ if (Flags.hubEditModeTouchAdjustments()) {
+ Alignment.Start
+ } else {
+ Alignment.CenterHorizontally
+ },
),
verticalAlignment = Alignment.CenterVertically,
) {
@@ -1193,6 +1260,7 @@ private fun CommunalContent(
is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier)
+ is CommunalContentModel.Spacer -> Box(Modifier.fillMaxSize())
}
}
@@ -1330,6 +1398,7 @@ private fun WidgetContent(
val shrinkWidgetLabel = stringResource(R.string.accessibility_action_label_shrink_widget)
val expandWidgetLabel = stringResource(R.string.accessibility_action_label_expand_widget)
+ val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
val selectedIndex =
selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
@@ -1443,7 +1512,8 @@ private fun WidgetContent(
) {
with(widgetSection) {
Widget(
- viewModel = viewModel,
+ isFocusable = isFocusable,
+ openWidgetEditor = { viewModel.onOpenWidgetEditor() },
model = model,
size = size,
modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
@@ -1723,24 +1793,22 @@ private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): Padd
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val screenHeight = with(density) { windowMetrics.bounds.height().toDp() }
val toolbarHeight = with(density) { Dimensions.ToolbarPaddingTop + toolbarSize.height.toDp() }
- val verticalPadding =
- ((screenHeight - toolbarHeight - hubDimensions.GridHeight + hubDimensions.GridTopSpacing) /
- 2)
- .coerceAtLeast(Dimensions.Spacing)
- return PaddingValues(
- start = Dimensions.ToolbarPaddingHorizontal,
- end = Dimensions.ToolbarPaddingHorizontal,
- top = verticalPadding + toolbarHeight,
- bottom = verticalPadding,
- )
-}
-
-@Composable
-private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx {
- return with(LocalDensity.current) {
- ContentPaddingInPx(
- start = paddingValues.calculateStartPadding(LocalLayoutDirection.current).toPx(),
- top = paddingValues.calculateTopPadding().toPx(),
+ return if (communalResponsiveGrid()) {
+ PaddingValues(
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ top = hubDimensions.GridTopSpacing,
+ )
+ } else {
+ val verticalPadding =
+ ((screenHeight - toolbarHeight - hubDimensions.GridHeight +
+ hubDimensions.GridTopSpacing) / 2)
+ .coerceAtLeast(Dimensions.Spacing)
+ PaddingValues(
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ top = verticalPadding + toolbarHeight,
+ bottom = verticalPadding,
)
}
}
@@ -1760,10 +1828,6 @@ private fun firstIndexAtOffset(gridState: LazyGridState, offset: Offset): Int? =
private fun keyAtIndexIfEditable(list: List<CommunalContentModel>, index: Int): String? =
if (index in list.indices && list[index].isWidgetContent()) list[index].key else null
-data class ContentPaddingInPx(val start: Float, val top: Float) {
- fun toOffset(): Offset = Offset(start, top)
-}
-
class Dimensions(val context: Context, val config: Configuration) {
val GridTopSpacing: Dp
get() {
@@ -1840,6 +1904,44 @@ private fun CommunalContentModel.getSpanOrMax(maxSpan: Int?) =
size.span
}
+private fun IntRect.percentOverlap(other: IntRect): Float {
+ val intersection = intersect(other)
+ if (intersection.width < 0 || intersection.height < 0) {
+ return 0f
+ }
+ val overlapArea = intersection.width * intersection.height
+ val area = width * height
+ return overlapArea.toFloat() / area.toFloat()
+}
+
+private fun calculatePercentVisible(state: LazyGridState, index: Int): Float {
+ val viewportSize = state.layoutInfo.viewportSize
+ val visibleRect =
+ IntRect(
+ offset =
+ IntOffset(
+ state.layoutInfo.viewportStartOffset + state.layoutInfo.beforeContentPadding,
+ 0,
+ ),
+ size =
+ IntSize(
+ width =
+ viewportSize.width -
+ state.layoutInfo.beforeContentPadding -
+ state.layoutInfo.afterContentPadding,
+ height = viewportSize.height,
+ ),
+ )
+
+ val itemInfo = state.layoutInfo.visibleItemsInfo.find { it.index == index }
+ return if (itemInfo != null) {
+ val boundingBox = IntRect(itemInfo.offset, itemInfo.size)
+ boundingBox.percentOverlap(visibleRect)
+ } else {
+ 0f
+ }
+}
+
private object Colors {
val DisabledColorFilter by lazy { disabledColorMatrix() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index 3642127d0823..c7930549abe8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -35,7 +35,11 @@ import androidx.compose.foundation.rememberOverscrollEffect
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.toComposeRect
import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
@@ -43,6 +47,7 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.coerceAtMost
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
+import androidx.window.layout.WindowMetricsCalculator
/**
* Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain
@@ -54,6 +59,7 @@ fun ResponsiveLazyHorizontalGrid(
cellAspectRatio: Float,
modifier: Modifier = Modifier,
state: LazyGridState = rememberLazyGridState(),
+ setContentOffset: (offset: Offset) -> Unit = {},
minContentPadding: PaddingValues = PaddingValues(0.dp),
minHorizontalArrangement: Dp = 0.dp,
minVerticalArrangement: Dp = 0.dp,
@@ -68,8 +74,9 @@ fun ResponsiveLazyHorizontalGrid(
"$minHorizontalArrangement and $minVerticalArrangement, respectively."
}
BoxWithConstraints(modifier) {
- val gridSize = rememberGridSize(maxWidth = maxWidth, maxHeight = maxHeight)
+ val gridSize = rememberGridSize()
val layoutDirection = LocalLayoutDirection.current
+ val density = LocalDensity.current
val minStartPadding = minContentPadding.calculateStartPadding(layoutDirection)
val minEndPadding = minContentPadding.calculateEndPadding(layoutDirection)
@@ -124,20 +131,43 @@ fun ResponsiveLazyHorizontalGrid(
val extraWidth = maxWidth - usedWidth
val extraHeight = maxHeight - usedHeight
+ // If there is a single column or single row, distribute extra space evenly across the grid.
+ // Otherwise, distribute it along the content padding to center the content.
+ val distributeHorizontalSpaceAlongGutters = gridSize.height == 1 || gridSize.width == 1
+ val evenlyDistributedWidth =
+ if (distributeHorizontalSpaceAlongGutters) {
+ extraWidth / (gridSize.width + 1)
+ } else {
+ extraWidth / 2
+ }
+
+ val finalStartPadding = minStartPadding + evenlyDistributedWidth
+ val finalEndPadding = minEndPadding + evenlyDistributedWidth
+ val finalTopPadding = minTopPadding + extraHeight / 2
+
val finalContentPadding =
PaddingValues(
- start = minStartPadding + extraWidth / 2,
- end = minEndPadding + extraWidth / 2,
- top = minTopPadding + extraHeight / 2,
+ start = finalStartPadding,
+ end = finalEndPadding,
+ top = finalTopPadding,
bottom = minBottomPadding + extraHeight / 2,
)
+ with(density) { setContentOffset(Offset(finalStartPadding.toPx(), finalTopPadding.toPx())) }
+
+ val horizontalArrangement =
+ if (distributeHorizontalSpaceAlongGutters) {
+ minHorizontalArrangement + evenlyDistributedWidth
+ } else {
+ minHorizontalArrangement
+ }
+
LazyHorizontalGrid(
rows = GridCells.Fixed(gridSize.height),
modifier = Modifier.fillMaxSize(),
state = state,
contentPadding = finalContentPadding,
- horizontalArrangement = Arrangement.spacedBy(minHorizontalArrangement),
+ horizontalArrangement = Arrangement.spacedBy(horizontalArrangement),
verticalArrangement = Arrangement.spacedBy(minVerticalArrangement),
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
@@ -179,12 +209,13 @@ private fun calculateClosestSize(maxWidth: Dp, maxHeight: Dp, aspectRatio: Float
* @property verticalArrangement The space between rows in the grid.
* @property gridSize The size of the grid, in cell units.
* @property availableHeight The maximum height an item in the grid may occupy.
+ * @property contentPadding The padding around the content of the grid.
*/
data class SizeInfo(
val cellSize: DpSize,
val verticalArrangement: Dp,
val gridSize: IntSize,
- private val contentPadding: PaddingValues,
+ val contentPadding: PaddingValues,
private val maxHeight: Dp,
) {
val availableHeight: Dp
@@ -200,27 +231,38 @@ data class SizeInfo(
}
@Composable
-private fun rememberGridSize(maxWidth: Dp, maxHeight: Dp): IntSize {
+private fun rememberGridSize(): IntSize {
val configuration = LocalConfiguration.current
val orientation = configuration.orientation
+ val screenSize = calculateWindowSize()
- return remember(orientation, maxWidth, maxHeight) {
+ return remember(orientation, screenSize) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
IntSize(
- width = calculateNumCellsWidth(maxWidth),
- height = calculateNumCellsHeight(maxHeight),
+ width = calculateNumCellsWidth(screenSize.width),
+ height = calculateNumCellsHeight(screenSize.height),
)
} else {
// In landscape we invert the rows/columns to ensure we match the same area as portrait.
// This keeps the number of elements in the grid consistent when changing orientation.
IntSize(
- width = calculateNumCellsHeight(maxWidth),
- height = calculateNumCellsWidth(maxHeight),
+ width = calculateNumCellsHeight(screenSize.width),
+ height = calculateNumCellsWidth(screenSize.height),
)
}
}
}
+@Composable
+fun calculateWindowSize(): DpSize {
+ // Observe view configuration changes and recalculate the size class on each change.
+ LocalConfiguration.current
+ val density = LocalDensity.current
+ val context = LocalContext.current
+ val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+ return with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
+}
+
private fun calculateNumCellsWidth(width: Dp) =
// See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
when {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
new file mode 100644
index 000000000000..9421596f7116
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.communal.ui.compose.section
+
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.PlatformIconButton
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class CommunalToDreamButtonSection
+@Inject
+constructor(
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
+ private val viewModelFactory: CommunalToDreamButtonViewModel.Factory,
+) {
+ @Composable
+ fun Button() {
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
+ return
+ }
+
+ val viewModel =
+ rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
+ val shouldShowDreamButtonOnHub by
+ viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)
+
+ if (!shouldShowDreamButtonOnHub) {
+ return
+ }
+
+ PlatformIconButton(
+ onClick = { viewModel.onShowDreamButtonTap() },
+ iconResource = R.drawable.ic_screensaver_auto,
+ contentDescription =
+ stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
+ colors =
+ IconButtonDefaults.filledIconButtonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ ),
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index 1475795e2dc6..d02215083679 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.composable
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -29,7 +30,9 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
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.graphics.Color
@@ -60,13 +63,25 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
@Composable
fun AlternateBouncer(
alternateBouncerDependencies: AlternateBouncerDependencies,
+ onHideAnimationFinished: () -> Unit,
modifier: Modifier = Modifier,
) {
val isVisible by
- alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(
- initialValue = false
- )
+ alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(true)
+ val visibleState = remember { MutableTransitionState(isVisible) }
+
+ // Feeds the isVisible value to the MutableTransitionState used by AnimatedVisibility below.
+ LaunchedEffect(isVisible) { visibleState.targetState = isVisible }
+
+ // Watches the MutableTransitionState and calls onHideAnimationFinished when the fade out
+ // animation is finished. This way the window view is removed from the view hierarchy only after
+ // the fade out animation is complete.
+ LaunchedEffect(visibleState.currentState, visibleState.isIdle) {
+ if (!visibleState.currentState && visibleState.isIdle) {
+ onHideAnimationFinished()
+ }
+ }
val udfpsIconLocation by
alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle(
@@ -74,7 +89,7 @@ fun AlternateBouncer(
)
AnimatedVisibility(
- visible = isVisible,
+ visibleState = visibleState,
enter = fadeIn(),
exit = fadeOut(),
modifier = modifier,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 364adcaffd77..5e9ade163ac2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -82,20 +82,15 @@ constructor(
Modifier.shortcutPadding()
} else {
Modifier
- }
+ },
)
}
}
}
@Composable
- fun SceneScope.IndicationArea(
- modifier: Modifier = Modifier,
- ) {
- Element(
- key = IndicationAreaElementKey,
- modifier = modifier.indicationAreaPadding(),
- ) {
+ fun SceneScope.IndicationArea(modifier: Modifier = Modifier) {
+ Element(key = IndicationAreaElementKey, modifier = modifier.indicationAreaPadding()) {
content {
IndicationArea(
indicationAreaViewModel = indicationAreaViewModel,
@@ -138,24 +133,20 @@ constructor(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.keyguard_bottom_affordance_bg,
- context.theme
+ context.theme,
)
foreground =
ResourcesCompat.getDrawable(
context.resources,
R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
+ context.theme,
)
visibility = View.INVISIBLE
setPadding(padding, padding, padding, padding)
}
setBinding(
- binder.bind(
- view,
- viewModel,
- transitionAlpha,
- ) {
+ binder.bind(view, viewModel, transitionAlpha) {
indicationController.showTransientIndication(it)
}
)
@@ -164,10 +155,7 @@ constructor(
},
onRelease = { binding?.destroy() },
modifier =
- modifier.size(
- width = shortcutSizeDp().width,
- height = shortcutSizeDp().height,
- )
+ modifier.size(width = shortcutSizeDp().width, height = shortcutSizeDp().height),
)
}
@@ -182,6 +170,8 @@ constructor(
AndroidView(
factory = { context ->
val view = KeyguardIndicationArea(context, null)
+ view.isFocusable = true
+ view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
setDisposable(
KeyguardIndicationAreaBinder.bind(
view = view,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index e78862e0e922..5c7ca97474b7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -29,11 +29,8 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -52,7 +49,6 @@ import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -179,16 +175,13 @@ constructor(
return
}
- val splitShadeTopMargin: Dp =
- LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
-
ConstrainedNotificationStack(
stackScrollView = stackScrollView.get(),
viewModel = rememberViewModel("Notifications") { viewModelFactory.create() },
modifier =
modifier
.fillMaxWidth()
- .thenIf(isShadeLayoutWide) { Modifier.padding(top = splitShadeTopMargin) }
+ .thenIf(isShadeLayoutWide) { Modifier.padding(top = 12.dp) }
.let {
if (burnInParams == null) {
it
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index da78eed2612b..1cee4d67df3b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -77,7 +77,7 @@ constructor(
resources.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin
)
- }
+ },
)
) {
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
@@ -87,6 +87,7 @@ constructor(
val paddingBelowClockStart =
dimensionResource(R.dimen.below_clock_padding_start)
val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+ val paddingCardHorizontal = paddingBelowClockEnd
if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
Row(
@@ -96,16 +97,14 @@ constructor(
// All items will be constrained to be as tall as the shortest
// item.
.height(IntrinsicSize.Min)
- .padding(
- start = paddingBelowClockStart,
- ),
+ .padding(start = paddingBelowClockStart),
) {
Date(
modifier =
Modifier.burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
- ),
+ )
)
Spacer(modifier = Modifier.width(4.dp))
Weather(
@@ -113,7 +112,7 @@ constructor(
Modifier.burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
- ),
+ )
)
}
}
@@ -121,14 +120,8 @@ constructor(
Card(
modifier =
Modifier.fillMaxWidth()
- .padding(
- start = paddingBelowClockStart,
- end = paddingBelowClockEnd,
- )
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
+ .padding(start = paddingCardHorizontal, end = paddingCardHorizontal)
+ .burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
)
}
}
@@ -136,9 +129,7 @@ constructor(
}
@Composable
- private fun Card(
- modifier: Modifier = Modifier,
- ) {
+ private fun Card(modifier: Modifier = Modifier) {
AndroidView(
factory = { context ->
FrameLayout(context).apply {
@@ -161,9 +152,7 @@ constructor(
}
@Composable
- private fun Weather(
- modifier: Modifier = Modifier,
- ) {
+ private fun Weather(modifier: Modifier = Modifier) {
val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
@@ -188,9 +177,7 @@ constructor(
}
@Composable
- private fun Date(
- modifier: Modifier = Modifier,
- ) {
+ private fun Date(modifier: Modifier = Modifier) {
val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
index 48067ce3c4a0..ef8911dae566 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
@@ -29,7 +29,7 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shared.model.ShadeMode
@@ -42,7 +42,7 @@ import kotlinx.coroutines.launch
* transition.
*/
@Composable
-fun SceneScope.NotificationLockscreenScrim(
+fun ContentScope.NotificationLockscreenScrim(
viewModel: NotificationLockscreenScrimViewModel,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index 94c18cdbef5a..cb87f0e7cf1c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -62,7 +62,6 @@ fun NotificationScrimNestedScrollConnection(
canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
},
- canStartPostFling = { false },
onStart = { firstScroll ->
onStart(firstScroll)
object : ScrollController {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index d8abfd7a4b94..e1ee59ba0626 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -25,11 +25,13 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+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.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.OnStopScope
@@ -80,9 +82,29 @@ fun Modifier.stackVerticalOverscroll(
}
return this.then(
- Modifier.nestedScroll(stackNestedScrollConnection).offset {
- IntOffset(x = 0, y = overscrollOffset.value.roundToInt())
- }
+ Modifier.nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPostFling(
+ consumed: Velocity,
+ available: Velocity,
+ ): Velocity {
+ return if (available.y < 0f && !canScrollForward()) {
+ overscrollOffset.animateTo(
+ targetValue = 0f,
+ initialVelocity = available.y,
+ animationSpec = tween(),
+ )
+ available
+ } else {
+ Velocity.Zero
+ }
+ }
+ }
+ }
+ )
+ .nestedScroll(stackNestedScrollConnection)
+ .offset { IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) }
)
}
@@ -100,7 +122,6 @@ fun NotificationStackNestedScrollConnection(
canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
- canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
onStart = { firstScroll ->
onStart(firstScroll)
object : ScrollController {
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 ae273d8e2ad9..b54de784a202 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
@@ -45,6 +45,7 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsBottomHeight
+import androidx.compose.foundation.overscroll
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
@@ -84,10 +85,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.NestedScrollBehavior
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
@@ -134,7 +135,7 @@ private val quickSettingsShadeContentKey: ContentKey
* entire size of the scene.
*/
@Composable
-fun SceneScope.HeadsUpNotificationSpace(
+fun ContentScope.HeadsUpNotificationSpace(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
useHunBounds: () -> Boolean = { true },
@@ -176,7 +177,7 @@ fun SceneScope.HeadsUpNotificationSpace(
* the user. When swiped up, the heads up notification is snoozed.
*/
@Composable
-fun SceneScope.SnoozeableHeadsUpNotificationSpace(
+fun ContentScope.SnoozeableHeadsUpNotificationSpace(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
) {
@@ -246,7 +247,7 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
/** Adds the space where notification stack should appear in the scene. */
@Composable
-fun SceneScope.ConstrainedNotificationStack(
+fun ContentScope.ConstrainedNotificationStack(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
@@ -281,7 +282,7 @@ fun SceneScope.ConstrainedNotificationStack(
*/
@OptIn(ExperimentalLayoutApi::class)
@Composable
-fun SceneScope.NotificationScrollingStack(
+fun ContentScope.NotificationScrollingStack(
shadeSession: SaveableSession,
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
@@ -480,6 +481,7 @@ fun SceneScope.NotificationScrollingStack(
modifier =
modifier
.element(Notifications.Elements.NotificationScrim)
+ .overscroll(verticalOverscrollEffect)
.offset {
// if scrim is expanded while transitioning to Gone or QS scene, increase the
// offset in step with the corresponding transition so that it is 0 when it
@@ -622,7 +624,7 @@ fun SceneScope.NotificationScrollingStack(
* the notification contents (stack, footer, shelf) should be drawn.
*/
@Composable
-fun SceneScope.NotificationStackCutoffGuideline(
+fun ContentScope.NotificationStackCutoffGuideline(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
@@ -642,7 +644,7 @@ fun SceneScope.NotificationStackCutoffGuideline(
}
@Composable
-private fun SceneScope.NotificationPlaceholder(
+private fun ContentScope.NotificationPlaceholder(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
useStackBounds: () -> Boolean,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 4f1acdc4da60..6591a75f2407 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -27,6 +27,7 @@ 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.layout.safeContentPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
@@ -95,7 +96,9 @@ private fun PeopleScreenWithConversations(
recentTiles: List<PeopleTileViewModel>,
onTileClicked: (PeopleTileViewModel) -> Unit,
) {
- Column(Modifier.sysuiResTag("top_level_with_conversations")) {
+ Column(
+ Modifier.fillMaxSize().safeContentPadding().sysuiResTag("top_level_with_conversations")
+ ) {
Column(
Modifier.fillMaxWidth().padding(PeopleSpacePadding),
horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index d483f88edfff..9f582bc4daa9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@@ -47,7 +48,7 @@ import com.android.systemui.res.R
@Composable
internal fun PeopleScreenEmpty(onGotItClicked: () -> Unit) {
Column(
- Modifier.fillMaxSize().padding(PeopleSpacePadding),
+ Modifier.fillMaxSize().safeContentPadding().padding(PeopleSpacePadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
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 2d32fd768eaa..f7ce2153b0ec 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
@@ -261,7 +261,7 @@ private fun RowScope.ForegroundServicesButton(
/** A button with an icon. */
@Composable
-private fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifier) {
+fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifier) {
Expandable(
color = colorAttr(model.backgroundColor),
shape = CircleShape,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 3fce89054b20..b1a19456ab7d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -26,8 +26,10 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -50,6 +52,8 @@ import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.TileDetails
import com.android.systemui.qs.panels.ui.compose.TileGrid
+import com.android.systemui.qs.panels.ui.compose.toolbar.Toolbar
+import com.android.systemui.qs.ui.composable.QuickSettingsShade.Dimensions.GridMaxHeight
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
@@ -122,7 +126,9 @@ constructor(
// A sealed interface to represent the possible states of the `ShadeBody`
sealed interface ShadeBodyState {
data object Editing : ShadeBodyState
+
data object TileDetails : ShadeBodyState
+
data object Default : ShadeBodyState
}
@@ -149,9 +155,8 @@ fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
ShadeBodyState.Editing -> {
EditMode(
viewModel = viewModel.editModeViewModel,
- modifier = Modifier
- .fillMaxWidth()
- .padding(QuickSettingsShade.Dimensions.Padding),
+ modifier =
+ Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
)
}
ShadeBodyState.TileDetails -> {
@@ -182,22 +187,23 @@ fun SceneScope.QuickSettingsLayout(
.padding(
start = QuickSettingsShade.Dimensions.Padding,
end = QuickSettingsShade.Dimensions.Padding,
- top = QuickSettingsShade.Dimensions.Padding,
+ bottom = QuickSettingsShade.Dimensions.Padding / 2,
),
) {
+ Toolbar(viewModel.toolbarViewModelFactory)
BrightnessSliderContainer(
viewModel = viewModel.brightnessSliderViewModel,
modifier =
Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
)
- Box {
+ Box(
+ modifier =
+ Modifier.requiredHeightIn(max = GridMaxHeight)
+ .verticalNestedScrollToScene()
+ .verticalScroll(rememberScrollState())
+ ) {
GridAnchor()
- TileGrid(
- viewModel = viewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
- )
+ TileGrid(viewModel = viewModel.tileGridViewModel, modifier = Modifier.fillMaxWidth())
}
}
}
@@ -207,6 +213,6 @@ object QuickSettingsShade {
object Dimensions {
val Padding = 16.dp
val BrightnessSliderHeight = 64.dp
- val GridMaxHeight = 800.dp
+ val GridMaxHeight = 420.dp
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 5fb9416cf35b..e4f4df386583 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -37,6 +37,7 @@ import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotific
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.viewmodel.GoneUserActionsViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -70,18 +71,22 @@ constructor(
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- val isIdle by remember {
- derivedStateOf { layoutState.transitionState is TransitionState.Idle }
+ val isIdleAndNotOccluded by remember {
+ derivedStateOf {
+ layoutState.transitionState is TransitionState.Idle &&
+ Overlays.NotificationsShade !in layoutState.transitionState.currentOverlays
+ }
}
- LaunchedEffect(isIdle) {
+ LaunchedEffect(isIdleAndNotOccluded) {
// Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon,
// and another transition could override the NSSL stack bounds.
- if (isIdle) {
+ if (isIdleAndNotOccluded) {
// Reset the stack bounds to avoid caching these values from the previous Scenes,
// and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE.
notificationStackScrolLView.get().apply {
- setStackTop(0f)
+ // use -headsUpInset to allow HUN translation outside bounds for snoozing
+ setStackTop(-getHeadsUpInset().toFloat())
setStackCutoff(0f)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 9de7a5d659ae..55fafd5cfeca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -33,7 +33,6 @@ import com.android.systemui.scene.ui.composable.transitions.notificationsShadeTo
import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition
-import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
/**
@@ -134,27 +133,11 @@ val SceneContainerTransitions = transitions {
}
// Scene overscroll
-
+ // TODO(b/382477212) Remove STL Overscroll DSL
overscrollDisabled(Scenes.Gone, Orientation.Vertical)
overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical)
- overscroll(Scenes.Bouncer, Orientation.Vertical) {
- translate(Bouncer.Elements.Content, y = { absoluteDistance })
- }
- overscroll(Scenes.Shade, Orientation.Vertical) {
- translate(
- Notifications.Elements.NotificationScrim,
- y = Shade.Dimensions.ScrimOverscrollLimit,
- )
- translate(Shade.Elements.SplitShadeStartColumn, y = Shade.Dimensions.ScrimOverscrollLimit)
- translate(
- Notifications.Elements.NotificationStackPlaceholder,
- y = Shade.Dimensions.ScrimOverscrollLimit,
- )
- }
- overscroll(Overlays.NotificationsShade, Orientation.Vertical) {
- translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
- }
- overscroll(Overlays.QuickSettingsShade, Orientation.Vertical) {
- translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
- }
+ overscrollDisabled(Scenes.Bouncer, Orientation.Vertical)
+ overscrollDisabled(Scenes.Shade, Orientation.Vertical)
+ overscrollDisabled(Overlays.NotificationsShade, Orientation.Vertical)
+ overscrollDisabled(Overlays.QuickSettingsShade, Orientation.Vertical)
}
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 46f5ecd99301..8a5c96da5ac6 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
@@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
import androidx.compose.foundation.layout.waterfall
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.overscroll
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
@@ -65,7 +66,10 @@ fun SceneScope.OverlayShade(
Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
Panel(
- modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
+ modifier =
+ Modifier.element(OverlayShade.Elements.Panel)
+ .overscroll(verticalOverscrollEffect)
+ .panelSize(),
content = content,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 22b6dbcf41ec..79fd1d7ddd8f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -39,6 +39,7 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.overscroll
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
@@ -527,6 +528,7 @@ private fun SceneScope.SplitShade(
Box(
modifier =
Modifier.element(Shade.Elements.SplitShadeStartColumn)
+ .overscroll(verticalOverscrollEffect)
.weight(1f)
.graphicsLayer { translationX = unfoldTranslationXForStartSide }
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index 6d03118645f3..0e35e1d83d6d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -45,6 +45,7 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
@@ -56,7 +57,7 @@ import kotlinx.coroutines.flow.StateFlow
/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
class ButtonComponent(
private val viewModelFlow: StateFlow<ButtonViewModel?>,
- private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit
+ private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit,
) : ComposeVolumePanelUiComponent {
@Composable
@@ -84,14 +85,26 @@ class ButtonComponent(
},
color =
if (viewModel.isActive) {
- MaterialTheme.colorScheme.tertiaryContainer
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.primary
+ } else {
+ MaterialTheme.colorScheme.tertiaryContainer
+ }
} else {
- MaterialTheme.colorScheme.surface
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.surfaceContainerHigh
+ } else {
+ MaterialTheme.colorScheme.surface
+ }
},
shape = RoundedCornerShape(20.dp),
contentColor =
if (viewModel.isActive) {
- MaterialTheme.colorScheme.onTertiaryContainer
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.onPrimary
+ } else {
+ MaterialTheme.colorScheme.onTertiaryContainer
+ }
} else {
MaterialTheme.colorScheme.onSurface
},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index bb2daecd3a25..2cd73040fa3f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.Flags
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.StateFlow
/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
private val viewModelFlow: StateFlow<ButtonViewModel?>,
- private val onCheckedChange: (isChecked: Boolean) -> Unit
+ private val onCheckedChange: (isChecked: Boolean) -> Unit,
) : ComposeVolumePanelUiComponent {
@Composable
@@ -68,15 +69,29 @@ class ToggleButtonComponent(
BottomComponentButtonSurface {
val colors =
if (viewModel.isActive) {
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- )
+ if (Flags.volumeRedesign()) {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ )
+ } else {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ )
+ }
} else {
- ButtonDefaults.buttonColors(
- containerColor = Color.Transparent,
- contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
- )
+ if (Flags.volumeRedesign()) {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ )
+ } else {
+ ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
}
Button(
modifier =
@@ -93,7 +108,7 @@ class ToggleButtonComponent(
onClick = { onCheckedChange(!viewModel.isActive) },
shape = RoundedCornerShape(20.dp),
colors = colors,
- contentPadding = PaddingValues(0.dp)
+ contentPadding = PaddingValues(0.dp),
) {
Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
}
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 581fb9d77c59..25892c5a75cc 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
@@ -37,11 +37,13 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
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
@@ -51,8 +53,11 @@ import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.PlatformIconButton
import com.android.compose.PlatformSliderColors
import com.android.compose.modifiers.padding
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
@@ -84,7 +89,11 @@ fun ColumnVolumeSliders(
val sliderPadding by topSliderPadding(isExpandable)
VolumeSlider(
- modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(),
+ modifier =
+ Modifier.thenIf(!Flags.volumeRedesign()) {
+ Modifier.padding(end = { sliderPadding.roundToPx() })
+ }
+ .fillMaxWidth(),
state = sliderState,
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
@@ -93,15 +102,29 @@ fun ColumnVolumeSliders(
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
hapticsViewModelFactory = sliderViewModel.getSliderHapticsViewModelFactory(),
+ button =
+ if (Flags.volumeRedesign()) {
+ {
+ ExpandButton(
+ isExpanded = isExpanded,
+ isExpandable = isExpandable,
+ onExpandedChanged = onExpandedChanged,
+ )
+ }
+ } else {
+ null
+ },
)
- ExpandButton(
- modifier = Modifier.align(Alignment.CenterEnd),
- isExpanded = isExpanded,
- isExpandable = isExpandable,
- onExpandedChanged = onExpandedChanged,
- sliderColors = sliderColors,
- )
+ if (!Flags.volumeRedesign()) {
+ ExpandButtonLegacy(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ isExpanded = isExpanded,
+ isExpandable = isExpandable,
+ onExpandedChanged = onExpandedChanged,
+ sliderColors = sliderColors,
+ )
+ }
}
AnimatedVisibility(
visible = isExpanded || !isExpandable,
@@ -153,7 +176,7 @@ fun ColumnVolumeSliders(
}
@Composable
-private fun ExpandButton(
+private fun ExpandButtonLegacy(
isExpanded: Boolean,
isExpandable: Boolean,
onExpandedChanged: (Boolean) -> Unit,
@@ -200,6 +223,48 @@ private fun ExpandButton(
}
}
+@Composable
+private fun ExpandButton(
+ isExpanded: Boolean,
+ isExpandable: Boolean,
+ onExpandedChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val expandButtonStateDescription =
+ if (isExpanded) {
+ stringResource(R.string.volume_panel_expanded_sliders)
+ } else {
+ stringResource(R.string.volume_panel_collapsed_sliders)
+ }
+ AnimatedVisibility(
+ modifier = modifier,
+ visible = isExpandable,
+ enter = expandButtonEnterTransition(),
+ exit = expandButtonExitTransition(),
+ ) {
+ PlatformIconButton(
+ modifier =
+ Modifier.size(width = 48.dp, height = 40.dp).semantics {
+ role = Role.Switch
+ stateDescription = expandButtonStateDescription
+ },
+ onClick = { onExpandedChanged(!isExpanded) },
+ colors =
+ IconButtonDefaults.iconButtonColors(
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ ),
+ iconResource =
+ if (isExpanded) {
+ R.drawable.ic_arrow_down_24dp
+ } else {
+ R.drawable.ic_arrow_up_24dp
+ },
+ contentDescription = null,
+ )
+ }
+}
+
private fun enterTransition(index: Int, totalCount: Int): EnterTransition {
val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0)
val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100)
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 97ce429cf938..fa5f72bc0997 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
@@ -24,9 +24,18 @@ import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
+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.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.MaterialTheme
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@@ -48,6 +57,7 @@ import androidx.compose.ui.semantics.stateDescription
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.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -61,11 +71,104 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl
fun VolumeSlider(
state: SliderState,
onValueChange: (newValue: Float) -> Unit,
- onValueChangeFinished: (() -> Unit)? = null,
onIconTapped: () -> Unit,
+ sliderColors: PlatformSliderColors,
modifier: Modifier = Modifier,
+ hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+ onValueChangeFinished: (() -> Unit)? = null,
+ button: (@Composable () -> Unit)? = null,
+) {
+ if (!Flags.volumeRedesign()) {
+ LegacyVolumeSlider(
+ state = state,
+ onValueChange = onValueChange,
+ onIconTapped = onIconTapped,
+ sliderColors = sliderColors,
+ onValueChangeFinished = onValueChangeFinished,
+ modifier = modifier,
+ hapticsViewModelFactory = hapticsViewModelFactory,
+ )
+ return
+ }
+
+ val value by valueState(state)
+ Column(modifier) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ state.icon?.let {
+ Icon(
+ icon = it,
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.size(40.dp).padding(8.dp),
+ )
+ }
+ Text(
+ text = state.label,
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.weight(1f).align(Alignment.CenterVertically),
+ )
+ button?.invoke()
+ }
+ Slider(
+ value = value,
+ valueRange = state.valueRange,
+ onValueChange = onValueChange,
+ onValueChangeFinished = onValueChangeFinished,
+ enabled = state.isEnabled,
+ modifier =
+ Modifier.height(40.dp).sysuiResTag(state.label).clearAndSetSemantics {
+ if (state.isEnabled) {
+ contentDescription = state.label
+ state.a11yClickDescription?.let {
+ customActions =
+ listOf(
+ CustomAccessibilityAction(it) {
+ onIconTapped()
+ true
+ }
+ )
+ }
+
+ state.a11yStateDescription?.let { stateDescription = it }
+ progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
+ } else {
+ disabled()
+ contentDescription =
+ state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
+ }
+ setProgress { targetValue ->
+ val targetDirection =
+ when {
+ targetValue > value -> 1
+ targetValue < value -> -1
+ else -> 0
+ }
+
+ val newValue =
+ (value + targetDirection * state.a11yStep).coerceIn(
+ state.valueRange.start,
+ state.valueRange.endInclusive,
+ )
+ onValueChange(newValue)
+ true
+ }
+ },
+ )
+ }
+}
+
+@Composable
+private fun LegacyVolumeSlider(
+ state: SliderState,
+ onValueChange: (newValue: Float) -> Unit,
+ onIconTapped: () -> Unit,
sliderColors: PlatformSliderColors,
hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+ modifier: Modifier = Modifier,
+ onValueChangeFinished: (() -> Unit)? = null,
) {
val value by valueState(state)
val interactionSource = remember { MutableInteractionSource() }
@@ -178,7 +281,7 @@ private fun valueState(state: SliderState): State<Float> {
val shouldSkipAnimation =
prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled
val value =
- if (shouldSkipAnimation) mutableFloatStateOf(state.value)
+ if (shouldSkipAnimation) remember { mutableFloatStateOf(state.value) }
else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation")
prevState = state
return value
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
index 4ae4eb875953..28226ff05ee9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
@@ -53,7 +53,7 @@ private enum class VolumeSliderContentComponent {
DisabledMessage,
}
-/** Shows label of the [VolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
+/** Shows label of the [LegacyVolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
@Composable
fun VolumeSliderContent(
label: String,
@@ -89,7 +89,7 @@ fun VolumeSliderContent(
}
}
},
- measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled)
+ measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled),
)
}
@@ -102,7 +102,7 @@ private class VolumeSliderContentMeasurePolicy(private val isEnabled: Boolean) :
override fun MeasureScope.measure(
measurables: List<Measurable>,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
val labelPlaceable =
measurables
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 0fc88b22a4d0..a4237f36ab58 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -225,7 +225,7 @@ fun ElementScope<*>.animateElementColorAsState(value: Color, key: ValueKey): Ani
return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
}
-private object SharedColorType : SharedValueType<Color, ColorDelta> {
+internal object SharedColorType : SharedValueType<Color, ColorDelta> {
override val unspecifiedValue: Color = Color.Unspecified
override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f)
@@ -255,17 +255,17 @@ private object SharedColorType : SharedValueType<Color, ColorDelta> {
alpha = aOklab.alpha + b.alpha * bWeight,
colorSpace = ColorSpaces.Oklab,
)
- .convert(aOklab.colorSpace)
+ .convert(a.colorSpace)
}
}
/**
- * Represents the diff between two colors in the same color space.
+ * Represents the diff between two colors in the Oklab color space.
*
* Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
* is internal.
*/
-private class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float)
+internal class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float)
@Composable
internal fun <T> animateSharedValueAsState(
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 caf5e41576f3..974442494181 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
@@ -23,12 +23,15 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.content.Content
-import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.TransitionState.DirectionProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
+import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.math.absoluteValue
+import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
internal interface DraggableHandler {
/**
@@ -105,7 +108,7 @@ internal class DraggableHandlerImpl(
swipes.updateSwipesResults(fromContent)
val result =
- swipes.findUserActionResult(overSlop)
+ (if (overSlop < 0f) swipes.upOrLeftResult else swipes.downOrRightResult)
// As we were unable to locate a valid target scene, the initial SwipeAnimation
// cannot be defined. Consequently, a simple NoOp Controller will be returned.
?: return NoOpDragController
@@ -191,9 +194,15 @@ private class DragControllerImpl(
private val draggableHandler: DraggableHandlerImpl,
val swipes: Swipes,
var swipeAnimation: SwipeAnimation<*>,
-) : DragController {
+) : DragController, SpaceVectorConverter by SpaceVectorConverter(draggableHandler.orientation) {
val layoutState = draggableHandler.layoutImpl.state
+ val overscrollableContent: OverscrollableContent =
+ when (draggableHandler.orientation) {
+ Orientation.Vertical -> draggableHandler.layoutImpl.verticalOverscrollableContent
+ Orientation.Horizontal -> draggableHandler.layoutImpl.horizontalOverscrollableContent
+ }
+
/**
* Whether this handle is active. If this returns false, calling [onDrag] and [onStop] will do
* nothing.
@@ -224,36 +233,75 @@ private class DragControllerImpl(
* @return the consumed delta
*/
override fun onDrag(delta: Float): Float {
- return onDrag(delta, swipeAnimation)
+ val initialAnimation = swipeAnimation
+ if (delta == 0f || !isDrivingTransition || initialAnimation.isAnimatingOffset()) {
+ return 0f
+ }
+ // swipeAnimation can change during the gesture, we want to always use the initial reference
+ // during the whole drag gesture.
+ return dragWithOverscroll(delta, animation = initialAnimation)
}
- private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
- if (delta == 0f || !isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
- return 0f
+ private fun <T : ContentKey> dragWithOverscroll(
+ delta: Float,
+ animation: SwipeAnimation<T>,
+ ): Float {
+ require(delta != 0f) { "delta should not be 0" }
+ var overscrollEffect = overscrollableContent.currentOverscrollEffect
+
+ // If we're already overscrolling, continue with the current effect for a smooth finish.
+ if (overscrollEffect == null || !overscrollEffect.isInProgress) {
+ // Otherwise, determine the target content (toContent or fromContent) for the new
+ // overscroll effect based on the gesture's direction.
+ val content = animation.contentByDirection(delta)
+ overscrollEffect = overscrollableContent.applyOverscrollEffectOn(content)
}
- val distance = swipeAnimation.distance()
- val previousOffset = swipeAnimation.dragOffset
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ if (!overscrollEffect.node.node.isAttached) {
+ return drag(delta, animation)
+ }
+
+ return overscrollEffect
+ .applyToScroll(
+ delta = delta.toOffset(),
+ source = NestedScrollSource.UserInput,
+ performScroll = {
+ val preScrollAvailable = it.toFloat()
+ drag(preScrollAvailable, animation).toOffset()
+ },
+ )
+ .toFloat()
+ }
+
+ private fun <T : ContentKey> drag(delta: Float, animation: SwipeAnimation<T>): Float {
+ if (delta == 0f) return 0f
+
+ val distance = animation.distance()
+ val previousOffset = animation.dragOffset
val desiredOffset = previousOffset + delta
- val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
+ val desiredProgress = animation.computeProgress(desiredOffset)
- // Note: the distance could be negative if fromContent is above or to the left of
- // toContent.
+ // Note: the distance could be negative if fromContent is above or to the left of toContent.
val newOffset =
when {
distance == DistanceUnspecified ||
- swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
+ animation.contentTransition.isWithinProgressRange(desiredProgress) ->
desiredOffset
distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
else -> desiredOffset.fastCoerceIn(distance, 0f)
}
- swipeAnimation.dragOffset = newOffset
+ animation.dragOffset = newOffset
return newOffset - previousOffset
}
override suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float {
- return onStop(velocity, canChangeContent, swipeAnimation)
+ // To ensure that any ongoing animation completes gracefully and avoids an undefined state,
+ // we execute the actual `onStop` logic in a non-cancellable context. This prevents the
+ // coroutine from being cancelled prematurely, which could interrupt the animation.
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ return withContext(NonCancellable) { onStop(velocity, canChangeContent, swipeAnimation) }
}
private suspend fun <T : ContentKey> onStop(
@@ -304,7 +352,22 @@ private class DragControllerImpl(
fromContent
}
- return swipeAnimation.animateOffset(velocity, targetContent)
+ val overscrollEffect = overscrollableContent.applyOverscrollEffectOn(targetContent)
+
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ if (!overscrollEffect.node.node.isAttached) {
+ return swipeAnimation.animateOffset(velocity, targetContent)
+ }
+
+ overscrollEffect.applyToFling(
+ velocity = velocity.toVelocity(),
+ performFling = {
+ val velocityLeft = it.toFloat()
+ swipeAnimation.animateOffset(velocityLeft, targetContent).toVelocity()
+ },
+ )
+
+ return velocity
}
/**
@@ -385,27 +448,6 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol
this.upOrLeftResult = upOrLeftResult
this.downOrRightResult = downOrRightResult
}
-
- /**
- * Returns the [UserActionResult] in the direction of [directionOffset].
- *
- * @param directionOffset signed float that indicates the direction. Positive is down or right
- * negative is up or left.
- * @return null when there are no targets in either direction. If one direction is null and you
- * drag into the null direction this function will return the opposite direction, assuming
- * that the users intention is to start the drag into the other direction eventually. If
- * [directionOffset] is 0f and both direction are available, it will default to
- * [upOrLeftResult].
- */
- fun findUserActionResult(directionOffset: Float): UserActionResult? {
- return when {
- upOrLeftResult == null && downOrRightResult == null -> null
- (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
- upOrLeftResult
-
- else -> downOrRightResult
- }
- }
}
internal class NestedScrollHandlerImpl(
@@ -473,31 +515,6 @@ internal class NestedScrollHandlerImpl(
}
}
},
- canStartPostFling = { velocityAvailable ->
- val behavior: NestedScrollBehavior =
- when {
- velocityAvailable > 0f -> topOrLeftBehavior
- velocityAvailable < 0f -> bottomOrRightBehavior
- else -> return@PriorityNestedScrollConnection false
- }
-
- // We could start an overscroll animation
- canChangeScene = false
-
- val pointersDown: PointersInfo.PointersDown? =
- when (val info = pointersInfoOwner.pointersInfo()) {
- PointersInfo.MouseWheel -> {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
-
- is PointersInfo.PointersDown -> info
- null -> null
- }
- lastPointersDown = pointersDown
-
- behavior.canStartOnPostFling && shouldEnableSwipes()
- },
onStart = { firstScroll ->
scrollController(
dragController =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index e819bfd18578..07a19d83c995 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -1257,7 +1257,7 @@ private inline fun <T> computeValue(
}
val currentContent = currentContentState.content
- if (transition is TransitionState.HasOverscrollProperties) {
+ if (transition is TransitionState.DirectionProperties) {
val overscroll = transition.currentOverscrollSpec
if (overscroll?.content == currentContent) {
val elementSpec =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 955be603efaf..9622fc151bb7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -32,7 +32,7 @@ import androidx.compose.ui.platform.InspectorInfo
* not consumed by the [SceneTransitionLayout] unless specifically requested via
* [nestedScrollToScene].
*/
-enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
+enum class NestedScrollBehavior {
/**
* Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
* gesture begins at the edge of the scrollable component (so that a scroll in that direction
@@ -42,7 +42,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- EdgeNoPreview(canStartOnPostFling = false),
+ EdgeNoPreview,
/**
* Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
@@ -52,7 +52,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- EdgeWithPreview(canStartOnPostFling = true),
+ @Deprecated("This will be removed, see b/378470603") EdgeWithPreview,
/**
* Any overscroll will be used by the [SceneTransitionLayout] to move to the next scene.
@@ -60,7 +60,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- EdgeAlways(canStartOnPostFling = true);
+ EdgeAlways;
companion object {
val Default = EdgeNoPreview
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index a14b2b3746f5..bf7e8e823658 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -36,6 +36,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.effect.ContentOverscrollEffect
/**
* [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -283,6 +284,53 @@ typealias SceneScope = ContentScope
@ElementDsl
interface ContentScope : BaseContentScope {
/**
+ * The overscroll effect applied to the content in the vertical direction. This can be used to
+ * customize how the content behaves when the scene is over scrolled.
+ *
+ * For example, you can use it with the `Modifier.overscroll()` modifier:
+ * ```kotlin
+ * @Composable
+ * fun ContentScope.MyScene() {
+ * Box(
+ * modifier = Modifier
+ * // Apply the effect
+ * .overscroll(verticalOverscrollEffect)
+ * ) {
+ * // ... your content ...
+ * }
+ * }
+ * ```
+ *
+ * Or you can read the `overscrollDistance` value directly, if you need some custom overscroll
+ * behavior:
+ * ```kotlin
+ * @Composable
+ * fun ContentScope.MyScene() {
+ * Box(
+ * modifier = Modifier
+ * .graphicsLayer {
+ * // Translate half of the overscroll
+ * translationY = verticalOverscrollEffect.overscrollDistance * 0.5f
+ * }
+ * ) {
+ * // ... your content ...
+ * }
+ * }
+ * ```
+ *
+ * @see horizontalOverscrollEffect
+ */
+ val verticalOverscrollEffect: ContentOverscrollEffect
+
+ /**
+ * The overscroll effect applied to the content in the horizontal direction. This can be used to
+ * customize how the content behaves when the scene is over scrolled.
+ *
+ * @see verticalOverscrollEffect
+ */
+ val horizontalOverscrollEffect: ContentOverscrollEffect
+
+ /**
* Animate some value at the content level.
*
* @param value the value of this shared value in the current content.
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 bdc1461f06c9..d7bac147d8f2 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
@@ -49,8 +49,10 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.Overlay
import com.android.compose.animation.scene.content.Scene
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.effect.GestureEffect
import com.android.compose.ui.util.lerp
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** The type for the content of movable elements. */
internal typealias MovableElementContent = @Composable (@Composable () -> Unit) -> Unit
@@ -134,6 +136,18 @@ internal class SceneTransitionLayoutImpl(
_movableContents = it
}
+ internal var horizontalOverscrollableContent =
+ OverscrollableContent(
+ animationScope = animationScope,
+ overscrollEffect = { content(it).scope.horizontalOverscrollGestureEffect },
+ )
+
+ internal var verticalOverscrollableContent =
+ OverscrollableContent(
+ animationScope = animationScope,
+ overscrollEffect = { content(it).scope.verticalOverscrollGestureEffect },
+ )
+
/**
* The different values of a shared value keyed by a a [ValueKey] and the different elements and
* contents it is associated to.
@@ -561,3 +575,23 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
return layout(width, height) { placeable.place(0, 0) }
}
}
+
+internal class OverscrollableContent(
+ private val animationScope: CoroutineScope,
+ private val overscrollEffect: (ContentKey) -> GestureEffect,
+) {
+ private var currentContent: ContentKey? = null
+ var currentOverscrollEffect: GestureEffect? = null
+
+ fun applyOverscrollEffectOn(contentKey: ContentKey): GestureEffect {
+ if (currentContent == contentKey) return currentOverscrollEffect!!
+
+ currentOverscrollEffect?.apply { animationScope.launch { ensureApplyToFlingIsCalled() } }
+
+ // We are wrapping the overscroll effect.
+ val overscrollEffect = overscrollEffect(contentKey)
+ currentContent = contentKey
+ currentOverscrollEffect = overscrollEffect
+ return overscrollEffect
+ }
+}
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 86c5fd824d8f..e8b2b09da377 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
@@ -382,7 +382,7 @@ internal class MutableSceneTransitionLayoutStateImpl(
// Compute the [TransformationSpec] when the transition starts.
val fromContent = transition.fromContent
val toContent = transition.toContent
- val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
+ val orientation = (transition as? TransitionState.DirectionProperties)?.orientation
// Update the transition specs.
transition.transformationSpec =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index ae235e5097af..5aaeda84edf0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -25,7 +25,7 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.TransitionState.DirectionProperties.Companion.DistanceUnspecified
import kotlin.math.absoluteValue
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.launch
@@ -197,7 +197,7 @@ internal class SwipeAnimation<T : ContentKey>(
private val distance: (SwipeAnimation<T>) -> Float,
currentContent: T = fromContent,
dragOffset: Float = 0f,
-) : TransitionState.HasOverscrollProperties {
+) : TransitionState.DirectionProperties {
/** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
lateinit var contentTransition: TransitionState.Transition
@@ -313,6 +313,17 @@ internal class SwipeAnimation<T : ContentKey>(
fun isAnimatingOffset(): Boolean = offsetAnimation != null
+ /** Get the [ContentKey] ([fromContent] or [toContent]) associated to the current [direction] */
+ fun contentByDirection(direction: Float): T {
+ require(direction != 0f) { "Cannot find a content in this direction: $direction" }
+ val isDirectionToContent = (isUpOrLeft && direction < 0) || (!isUpOrLeft && direction > 0)
+ return if (isDirectionToContent) {
+ toContent
+ } else {
+ fromContent
+ }
+ }
+
/**
* Animate the offset to a [targetContent], using the [initialVelocity] and an optional [spec]
*
@@ -326,13 +337,6 @@ internal class SwipeAnimation<T : ContentKey>(
check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }
val initialProgress = progress
- // Skip the animation if we have already reached the target content and the overscroll does
- // not animate anything.
- val hasReachedTargetContent =
- (targetContent == toContent && initialProgress >= 1f) ||
- (targetContent == fromContent && initialProgress <= 0f)
- val skipAnimation =
- hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
val targetContent =
if (targetContent != currentContent && !canChangeContent(targetContent)) {
@@ -341,6 +345,14 @@ internal class SwipeAnimation<T : ContentKey>(
targetContent
}
+ // Skip the animation if we have already reached the target content and the overscroll does
+ // not animate anything.
+ val hasReachedTargetContent =
+ (targetContent == toContent && initialProgress >= 1f) ||
+ (targetContent == fromContent && initialProgress <= 0f)
+ val skipAnimation =
+ hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
+
val targetOffset =
if (targetContent == fromContent) {
0f
@@ -501,7 +513,7 @@ private class ChangeSceneSwipeTransition(
swipeAnimation.toContent,
replacedTransition,
),
- TransitionState.HasOverscrollProperties by swipeAnimation {
+ TransitionState.DirectionProperties by swipeAnimation {
constructor(
other: ChangeSceneSwipeTransition
@@ -563,7 +575,7 @@ private class ShowOrHideOverlaySwipeTransition(
swipeAnimation.toContent,
replacedTransition,
),
- TransitionState.HasOverscrollProperties by swipeAnimation {
+ TransitionState.DirectionProperties by swipeAnimation {
constructor(
other: ShowOrHideOverlaySwipeTransition
) : this(
@@ -622,7 +634,7 @@ private class ReplaceOverlaySwipeTransition(
swipeAnimation.toContent,
replacedTransition,
),
- TransitionState.HasOverscrollProperties by swipeAnimation {
+ TransitionState.DirectionProperties by swipeAnimation {
constructor(
other: ReplaceOverlaySwipeTransition
) : this(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 48f08a7086d6..952668ab49ff 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -111,6 +111,9 @@ interface SceneTransitionsBuilder {
* The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
* [distance] down/right, -1f when moving in the opposite direction.
*/
+ @Deprecated(
+ "Use verticalOverscrollEffect (or horizontalOverscrollEffect) directly from SceneScope."
+ )
fun overscroll(
content: ContentKey,
orientation: Orientation,
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 8c4cd8c93b87..152f05eb5cc7 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
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene.content
import android.annotation.SuppressLint
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
@@ -51,6 +52,9 @@ import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSharedValueAsState
+import com.android.compose.animation.scene.effect.GestureEffect
+import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
+import com.android.compose.animation.scene.effect.VisualEffect
import com.android.compose.animation.scene.element
import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
import com.android.compose.animation.scene.nestedScrollToScene
@@ -109,6 +113,26 @@ internal class ContentScopeImpl(
override val layoutState: SceneTransitionLayoutState = layoutImpl.state
+ private val _verticalOverscrollEffect =
+ OffsetOverscrollEffect(
+ orientation = Orientation.Vertical,
+ animationScope = layoutImpl.animationScope,
+ )
+
+ private val _horizontalOverscrollEffect =
+ OffsetOverscrollEffect(
+ orientation = Orientation.Horizontal,
+ animationScope = layoutImpl.animationScope,
+ )
+
+ val verticalOverscrollGestureEffect = GestureEffect(_verticalOverscrollEffect)
+
+ val horizontalOverscrollGestureEffect = GestureEffect(_horizontalOverscrollEffect)
+
+ override val verticalOverscrollEffect = VisualEffect(_verticalOverscrollEffect)
+
+ override val horizontalOverscrollEffect = VisualEffect(_horizontalOverscrollEffect)
+
override fun Modifier.element(key: ElementKey): Modifier {
return element(layoutImpl, content, key)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index d66fe42084de..29be445e82bb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -273,7 +273,7 @@ sealed interface TransitionState {
* every time progress is changed.
*/
private val _currentOverscrollSpec: State<OverscrollSpecImpl?>? =
- if (this !is HasOverscrollProperties) {
+ if (this !is DirectionProperties) {
null
} else {
derivedStateOf {
@@ -406,7 +406,7 @@ sealed interface TransitionState {
/** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */
internal fun isWithinProgressRange(progress: Float): Boolean {
// If the properties are missing we assume that every [Transition] can overscroll
- if (this !is HasOverscrollProperties) return true
+ if (this !is DirectionProperties) return true
// [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet.
val specForCurrentScene =
when {
@@ -444,7 +444,7 @@ sealed interface TransitionState {
}
}
- interface HasOverscrollProperties {
+ interface DirectionProperties {
/**
* The position of the [Transition.toContent].
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
new file mode 100644
index 000000000000..2233debde277
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.compose.animation.scene.effect
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.unit.Velocity
+import com.android.compose.ui.util.SpaceVectorConverter
+import kotlin.math.abs
+import kotlin.math.sign
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * An [OverscrollEffect] that uses an [Animatable] to track and animate overscroll values along a
+ * specific [Orientation].
+ */
+interface ContentOverscrollEffect : OverscrollEffect {
+ /** The current overscroll value. */
+ val overscrollDistance: Float
+}
+
+open class BaseContentOverscrollEffect(
+ orientation: Orientation,
+ private val animationScope: CoroutineScope,
+ private val animationSpec: AnimationSpec<Float>,
+) : ContentOverscrollEffect, SpaceVectorConverter by SpaceVectorConverter(orientation) {
+
+ /** The [Animatable] that holds the current overscroll value. */
+ private val animatable = Animatable(initialValue = 0f, visibilityThreshold = 0.5f)
+
+ override val overscrollDistance: Float
+ get() = animatable.value
+
+ override val isInProgress: Boolean
+ get() = overscrollDistance != 0f
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ val deltaForAxis = delta.toFloat()
+
+ // If we're currently overscrolled, and the user scrolls in the opposite direction, we need
+ // to "relax" the overscroll by consuming some of the scroll delta to bring it back towards
+ // zero.
+ val currentOffset = animatable.value
+ val sameDirection = deltaForAxis.sign == currentOffset.sign
+ val consumedByPreScroll =
+ if (abs(currentOffset) > 0.5 && !sameDirection) {
+ // The user has scrolled in the opposite direction.
+ val prevOverscrollValue = currentOffset
+ val newOverscrollValue = currentOffset + deltaForAxis
+ if (sign(prevOverscrollValue) != sign(newOverscrollValue)) {
+ // Enough to completely cancel the overscroll. We snap the overscroll value
+ // back to zero and consume the corresponding amount of the scroll delta.
+ animationScope.launch { animatable.snapTo(0f) }
+ -prevOverscrollValue
+ } else {
+ // Not enough to cancel the overscroll. We update the overscroll value
+ // accordingly and consume the entire scroll delta.
+ animationScope.launch { animatable.snapTo(newOverscrollValue) }
+ deltaForAxis
+ }
+ } else {
+ 0f
+ }
+ .toOffset()
+
+ // After handling any overscroll relaxation, we pass the remaining scroll delta to the
+ // standard scrolling logic.
+ val leftForScroll = delta - consumedByPreScroll
+ val consumedByScroll = performScroll(leftForScroll)
+ val overscrollDelta = leftForScroll - consumedByScroll
+
+ // If the user is dragging (not flinging), and there's any remaining scroll delta after the
+ // standard scrolling logic has been applied, we add it to the overscroll.
+ if (abs(overscrollDelta.toFloat()) > 0.5 && source == NestedScrollSource.UserInput) {
+ animationScope.launch { animatable.snapTo(currentOffset + overscrollDelta.toFloat()) }
+ }
+
+ return delta
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ // We launch a coroutine to ensure the fling animation starts after any pending [snapTo]
+ // animations have finished.
+ // This guarantees a smooth, sequential execution of animations on the overscroll value.
+ coroutineScope {
+ launch {
+ val consumed = performFling(velocity)
+ val remaining = velocity - consumed
+ animatable.animateTo(0f, animationSpec, remaining.toFloat())
+ }
+ }
+ }
+}
+
+/** An overscroll effect that ensures only a single fling animation is triggered. */
+internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
+ ContentOverscrollEffect by delegate {
+ private var shouldFling = false
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ shouldFling = true
+ return delegate.applyToScroll(delta, source, performScroll)
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ if (!shouldFling) {
+ performFling(velocity)
+ return
+ }
+ shouldFling = false
+ delegate.applyToFling(velocity, performFling)
+ }
+
+ suspend fun ensureApplyToFlingIsCalled() {
+ applyToFling(Velocity.Zero) { Velocity.Zero }
+ }
+}
+
+/**
+ * An overscroll effect that only applies visual effects and does not interfere with the actual
+ * scrolling or flinging behavior.
+ */
+internal class VisualEffect(private val delegate: ContentOverscrollEffect) :
+ ContentOverscrollEffect by delegate {
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ return performScroll(delta)
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ performFling(velocity)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
new file mode 100644
index 000000000000..f459c46d3e6f
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.compose.animation.scene.effect
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ProgressConverter
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+
+/** An [OverscrollEffect] that offsets the content by the overscroll value. */
+class OffsetOverscrollEffect(
+ orientation: Orientation,
+ animationScope: CoroutineScope,
+ animationSpec: AnimationSpec<Float> = DefaultAnimationSpec,
+) : BaseContentOverscrollEffect(orientation, animationScope, animationSpec) {
+ private var _node: DelegatableNode = newNode()
+ override val node: DelegatableNode
+ get() = _node
+
+ fun newNode(): DelegatableNode {
+ return object : Modifier.Node(), LayoutModifierNode {
+ override fun onDetach() {
+ super.onDetach()
+ // TODO(b/379086317) Remove this workaround: avoid to reuse the same node.
+ _node = newNode()
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ val offsetPx = computeOffset(density = this@measure, overscrollDistance)
+ placeable.placeRelativeWithLayer(position = offsetPx.toIntOffset())
+ }
+ }
+ }
+ }
+
+ companion object {
+ private val MaxDistance = 400.dp
+
+ internal val DefaultAnimationSpec =
+ spring(
+ stiffness = Spring.StiffnessLow,
+ dampingRatio = Spring.DampingRatioLowBouncy,
+ visibilityThreshold = 0.5f,
+ )
+
+ @VisibleForTesting
+ internal fun computeOffset(density: Density, overscrollDistance: Float): Int {
+ val maxDistancePx = with(density) { MaxDistance.toPx() }
+ val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx)
+ return (progress * maxDistancePx).roundToInt()
+ }
+ }
+}
+
+@Composable
+fun rememberOffsetOverscrollEffect(
+ orientation: Orientation,
+ animationSpec: AnimationSpec<Float> = OffsetOverscrollEffect.DefaultAnimationSpec,
+): OffsetOverscrollEffect {
+ val animationScope = rememberCoroutineScope()
+ return remember(orientation, animationScope, animationSpec) {
+ OffsetOverscrollEffect(orientation, animationScope, animationSpec)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index bfb5ca733d90..944bd85991c9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -157,7 +157,7 @@ private class VerticalContainerRevealSizeTransformation(
val idleSize = checkNotNull(element.targetSize(content))
val userActionDistance = idleSize.height
val progress =
- when ((transition as? TransitionState.HasOverscrollProperties)?.bouncingContent) {
+ when ((transition as? TransitionState.DirectionProperties)?.bouncingContent) {
null -> transition.progressTo(content)
content -> 1f
else -> 0f
@@ -256,7 +256,7 @@ private class ContainerRevealAlphaTransformation(
private fun targetAlpha(transition: TransitionState.Transition, content: ContentKey): Float {
if (transition.isUserInputOngoing) {
- if (transition !is TransitionState.HasOverscrollProperties) {
+ if (transition !is TransitionState.DirectionProperties) {
error(
"Unsupported transition driven by user input but that does not have " +
"overscroll properties: $transition"
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 2f4d5bff8b41..432add38385a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -60,7 +60,7 @@ private constructor(
// As this object is created by OverscrollBuilderImpl and we retrieve the current
// OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume
// that this method was invoked after performing this check.
- val overscrollProperties = transition as TransitionState.HasOverscrollProperties
+ val overscrollProperties = transition as TransitionState.DirectionProperties
val overscrollScope =
cachedOverscrollScope.getFromCacheOrCompute(density = this, overscrollProperties)
@@ -77,17 +77,17 @@ private constructor(
/**
* A helper class to cache a [OverscrollScope] given a [Density] and
- * [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame
- * whenever an overscroll transition is computed.
+ * [TransitionState.DirectionProperties]. This helps avoid recreating a scope every frame whenever
+ * an overscroll transition is computed.
*/
private class CachedOverscrollScope {
private var previousScope: OverscrollScope? = null
private var previousDensity: Density? = null
- private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null
+ private var previousOverscrollProperties: TransitionState.DirectionProperties? = null
fun getFromCacheOrCompute(
density: Density,
- overscrollProperties: TransitionState.HasOverscrollProperties,
+ overscrollProperties: TransitionState.DirectionProperties,
): OverscrollScope {
if (
previousScope == null ||
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index a5be4dc195bc..b26bf55c85ec 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -48,15 +48,14 @@ fun LargeTopAppBarNestedScrollConnection(
orientation = Orientation.Vertical,
// When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
// expand. Then, you can then scroll down the content.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
- offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight()
+ canStartPreScroll = { offsetAvailable, _, _ ->
+ offsetAvailable < 0 && height() > minHeight()
},
// When swiping down, the content will scroll up until it reaches the top. Then, the
// LargeTopAppBar will expand until it reaches its [maxHeight].
canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && height() < maxHeight()
},
- canStartPostFling = { false },
onStart = {
LargeTopAppBarScrollController(
height = height,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 3f182363e20c..3d0f182fffee 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -24,7 +24,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.math.sign
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@@ -102,8 +101,8 @@ interface OnStopScope {
* over the default nested scrolling logic.
*
* When started, this connection intercepts scroll events *before* they reach child composables.
- * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll]
- * or [canStartPostFling] returns `true`.
+ * This "priority mode" is activated when either [canStartPreScroll] or [canStartPostScroll] returns
+ * `true`.
*
* Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This
* controller allows you to directly manipulate the scroll state and define how scroll events are
@@ -123,8 +122,6 @@ interface OnStopScope {
* @param canStartPostScroll A lambda that returns `true` if the connection should enter priority
* mode during the post-scroll phase. This is called after child connections have consumed the
* scroll.
- * @param canStartPostFling A lambda that returns `true` if the connection should enter priority
- * mode during the post-fling phase. This is called after a fling gesture has been initiated.
* @param onStart A lambda that is called when the connection enters priority mode. It should return
* a [ScrollController] that will be used to control the scroll.
* @sample LargeTopAppBarNestedScrollConnection
@@ -136,7 +133,6 @@ class PriorityNestedScrollConnection(
(offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
private val canStartPostScroll:
(offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
- private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
private val onStart: (firstScroll: Float) -> ScrollController,
) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
@@ -233,17 +229,6 @@ class PriorityNestedScrollConnection(
return stop(velocity = availableFloat)
}
- // Check if post-fling condition is met, and start priority mode if necessary.
- // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
- // overscroll behavior on the Scene level.
- if (canStartPostFling(availableFloat)) {
- // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of
- // 1px given the available velocity.
- val smallOffset = availableFloat.sign
- start(availableOffset = smallOffset)
- return stop(availableFloat)
- }
-
// Reset offset tracking after the fling gesture is finished.
resetOffsetTracker()
return Velocity.Zero
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 3644b3069fb3..2fd1d8d8573a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -18,7 +18,10 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
@@ -495,4 +498,13 @@ class AnimatedSharedAsStateTest {
assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f)
assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f)
}
+
+ @Test
+ fun interpolatedColor() {
+ val a = Color.Red
+ val b = Color.Green
+ val delta = SharedColorType.diff(b, a) // b - a
+ val interpolated = SharedColorType.addWeighted(a, delta, 0.5f) // a + (b - a) * 0.5f
+ rule.setContent { Box(Modifier.fillMaxSize().background(interpolated)) }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 2c8dc3264b7e..6985644579f6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -19,7 +19,9 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.overscroll
import androidx.compose.material3.Text
+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.Companion.UserInput
@@ -42,7 +44,6 @@ import com.android.compose.animation.scene.content.state.TransitionState.Transit
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
-import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
@@ -103,7 +104,7 @@ class DraggableHandlerTest {
userActions =
mapOf(Swipe.Up to SceneB, Swipe.Up(fromSource = Edge.Bottom) to SceneA),
) {
- Text("SceneC")
+ Text("SceneC", Modifier.overscroll(verticalOverscrollEffect))
}
overlay(
key = OverlayA,
@@ -435,35 +436,12 @@ class DraggableHandlerTest {
}
@Test
- fun onDragIntoNoAction_startTransitionToOppositeDirection() = runGestureTest {
+ fun onDragIntoNoAction_stayIdle() = runGestureTest {
navigateToSceneC()
// We are on SceneC which has no action in Down direction
- val dragController = onDragStarted(overSlop = 10f)
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = -0.1f,
- )
-
- // Reverse drag direction, it will consume the previous drag
- dragController.onDragDelta(pixels = -10f)
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.0f,
- )
-
- // Continue reverse drag direction, it should record progress to Scene B
- dragController.onDragDelta(pixels = -10f)
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.1f,
- )
+ onDragStarted(overSlop = 10f, expectedConsumedOverSlop = 0f)
+ assertIdle(currentScene = SceneC)
}
@Test
@@ -921,27 +899,25 @@ class DraggableHandlerTest {
}
@Test
- fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest {
- layoutState.transitions = transitions {
- overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
- }
- // Start at scene C.
- navigateToSceneC()
+ fun blockTransition_animated() = runGestureTest {
+ assertIdle(SceneA)
+ layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) }
- val scene = layoutState.transitionState.currentScene
- // We should have overscroll spec for scene C
- assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull()
- assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNull()
+ // Swipe up to scene B. Overscroll 50%.
+ val dragController = onDragStarted(overSlop = up(1.5f), expectedConsumedOverSlop = up(1.0f))
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB, progress = 1f)
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
- nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
+ // Block the transition when the user release their finger.
+ canChangeScene = { false }
+ val velocityConsumed =
+ dragController.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- // We scrolled down, under scene C there is nothing, so we can use the overscroll spec
- assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNotNull()
- assertThat(layoutState.currentTransition?.currentOverscrollSpec?.content).isEqualTo(SceneC)
- val transition = layoutState.currentTransition
- assertThat(transition).isNotNull()
- assertThat(transition!!.progress).isEqualTo(-0.1f)
+ // Start an animation: overscroll and from 1f to 0f.
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB, progress = 1f)
+
+ val consumed = velocityConsumed.await()
+ assertThat(consumed).isEqualTo(-velocityThreshold)
+ assertIdle(SceneA)
}
@Test
@@ -1208,72 +1184,6 @@ class DraggableHandlerTest {
}
@Test
- fun overscroll_releaseAtNegativePercent_up() = runGestureTest {
- // Make Scene A overscrollable.
- layoutState.transitions = transitions {
- from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
- overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
- }
-
- mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB))
-
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1f))
- val transition = assertThat(transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(SceneB)
- assertThat(transition).hasProgress(-1f)
-
- // Release to A.
- dragController.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = {
- assertTransition(fromScene = SceneA, toScene = SceneB, progress = -1f)
- },
- expectedConsumedVelocity = 0f,
- )
-
- // We kept the overscroll at 100% so that the placement logic didn't change at the end of
- // the animation.
- assertIdle(SceneA)
- assertThat(transition).hasProgress(0f)
- assertThat(transition).hasOverscrollSpec()
- }
-
- @Test
- fun overscroll_releaseAtNegativePercent_down() = runGestureTest {
- // Make Scene A overscrollable.
- layoutState.transitions = transitions {
- from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
- overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
- }
-
- mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC))
-
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1f))
- val transition = assertThat(transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(SceneC)
- assertThat(transition).hasProgress(-1f)
-
- // Release to A.
- dragController.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = {
- assertTransition(fromScene = SceneA, toScene = SceneC, progress = -1f)
- },
- expectedConsumedVelocity = 0f,
- )
-
- // We kept the overscroll at 100% so that the placement logic didn't change at the end of
- // the animation.
- assertIdle(SceneA)
- assertThat(transition).hasProgress(0f)
- assertThat(transition).hasOverscrollSpec()
- }
-
- @Test
fun requireFullDistanceSwipe() = runGestureTest {
mutableUserActionsA +=
Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index a301856d024f..ffba63988cfc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.overscroll
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.Text
@@ -47,6 +48,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
@@ -60,6 +62,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
@@ -72,6 +75,7 @@ import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.setContentAndCreateMainScope
@@ -712,7 +716,7 @@ class ElementTest {
}
@Test
- fun elementTransitionDuringOverscroll() {
+ fun elementTransitionDuringOverscrollWithOverscrollDSL() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
val overscrollTranslateY = 10.dp
@@ -765,79 +769,311 @@ class ElementTest {
assertThat(animatedFloat).isEqualTo(100f)
}
+ private fun expectedOffset(currentOffset: Dp, density: Density): Dp {
+ return with(density) {
+ OffsetOverscrollEffect.computeOffset(density, currentOffset.toPx()).toDp()
+ }
+ }
+
@Test
- fun elementTransitionDuringNestedScrollOverscroll() {
+ fun elementTransitionDuringOverscroll() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ lateinit var density: Density
+
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
var touchSlop = 0f
- val overscrollTranslateY = 10.dp
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) },
+ )
+ }
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ // Scroll 50%.
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ rule
+ .onNodeWithTag(TestElements.Foo.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+ }
+
+ @Test
+ fun elementTransitionOverscrollMultipleScenes() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
+ lateinit var density: Density
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
val state =
rule.runOnUiThread {
MutableSceneTransitionLayoutState(
- initialScene = SceneB,
+ initialScene = SceneA,
transitions =
transitions {
- overscroll(SceneB, Orientation.Vertical) {
- progressConverter = ProgressConverter.linear()
- translate(TestElements.Foo, y = overscrollTranslateY)
- }
+ overscrollDisabled(SceneA, Orientation.Vertical)
+ overscrollDisabled(SceneB, Orientation.Vertical)
},
)
- as MutableSceneTransitionLayoutStateImpl
}
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Bar)
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f // Scroll 50%
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule
+ .onNodeWithTag(TestElements.Bar.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+
+ rule.onRoot().performTouchInput {
+ // Scroll another -30%.
+ moveBy(Offset(0f, layoutHeight.toPx() * -0.3f), delayMillis = 1_000)
+ }
+
+ // Scroll 120% (Scene B overscroll by 20%).
+ assertThat(transition).hasProgress(1f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule
+ .onNodeWithTag(TestElements.Bar.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.2f, density))
+ rule.onRoot().performTouchInput {
+ // Scroll another -70%
+ moveBy(Offset(0f, layoutHeight.toPx() * -0.7f), delayMillis = 1_000)
+ }
+ // Scroll 50% (No overscroll).
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another -100%.
+ moveBy(Offset(0f, layoutHeight.toPx() * -1f), delayMillis = 1_000)
+ }
+
+ // Scroll -50% (Scene A overscroll by -50%).
+ assertThat(transition).hasProgress(0f)
+ rule
+ .onNodeWithTag(TestElements.Foo.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * -0.5f, density))
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun elementTransitionOverscroll() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ lateinit var density: Density
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ defaultOverscrollProgressConverter = ProgressConverter.linear()
+ overscrollDisabled(SceneB, Orientation.Vertical)
+ },
+ )
+ }
rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(TestElements.Foo)
+ .fillMaxSize()
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f // Scroll 50%
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ fooElement.assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+ }
+
+ @Test
+ fun elementTransitionDuringNestedScrollOverscroll() {
+ lateinit var density: Density
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) },
+ )
+ }
+
+ rule.setContent {
+ density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(
state = state,
modifier = Modifier.size(layoutWidth, layoutHeight),
) {
- scene(SceneA) { Spacer(Modifier.fillMaxSize()) }
- scene(SceneB, userActions = mapOf(Swipe.Up to SceneA)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
Box(
Modifier
// A scrollable that does not consume the scroll gesture
.scrollable(
- rememberScrollableState(consumeScrollDelta = { 0f }),
- Orientation.Vertical,
+ state = rememberScrollableState(consumeScrollDelta = { 0f }),
+ orientation = Orientation.Vertical,
)
.fillMaxSize()
- ) {
- Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
- }
+ )
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(TestElements.Foo)
+ .fillMaxSize()
+ )
}
}
}
assertThat(state.transitionState).isIdle()
- val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
- fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertDoesNotExist()
// Swipe by half of verticalSwipeDistance.
rule.onRoot().performTouchInput {
val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
down(middleTop)
- // Scroll 50%
+ // Scroll 50%.
moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
}
val transition = assertThat(state.transitionState).isSceneTransition()
- assertThat(transition).hasOverscrollSpec()
- assertThat(transition).hasProgress(-0.5f)
- fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
+ assertThat(transition).hasProgress(0.5f)
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
rule.onRoot().performTouchInput {
- // Scroll another 100%
+ // Scroll another 100%.
moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
}
- // Scroll 150% (Scene B overscroll by 50%)
- assertThat(transition).hasProgress(-1.5f)
- assertThat(transition).hasOverscrollSpec()
- fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+ rule
+ .onNodeWithTag(TestElements.Foo.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
}
@Test
@@ -2617,7 +2853,7 @@ class ElementTest {
// Start an overscrollable transition driven by progress.
var progress by mutableFloatStateOf(0f)
val transition = transition(from = SceneA, to = SceneB, progress = { progress })
- assertThat(transition).isInstanceOf(TransitionState.HasOverscrollProperties::class.java)
+ assertThat(transition).isInstanceOf(TransitionState.DirectionProperties::class.java)
scope.launch { state.startTransition(transition) }
// Reset the counters after the first animation frame.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
new file mode 100644
index 000000000000..da8fe3094448
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.compose.animation.scene.effect
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.overscroll
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlin.properties.Delegates
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class OffsetOverscrollEffectTest {
+ @get:Rule val rule = createComposeRule()
+
+ private val BOX_TAG = "box"
+
+ private data class LayoutInfo(val layoutSize: Dp, val touchSlop: Float, val density: Density) {
+ fun expectedOffset(currentOffset: Dp): Dp {
+ return with(density) {
+ OffsetOverscrollEffect.computeOffset(this, currentOffset.toPx()).toDp()
+ }
+ }
+ }
+
+ private fun setupOverscrollableBox(
+ scrollableOrientation: Orientation,
+ overscrollEffectOrientation: Orientation = scrollableOrientation,
+ ): LayoutInfo {
+ val layoutSize: Dp = 200.dp
+ var touchSlop: Float by Delegates.notNull()
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ lateinit var density: Density
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ val overscrollEffect = rememberOffsetOverscrollEffect(overscrollEffectOrientation)
+
+ Box(
+ Modifier.overscroll(overscrollEffect)
+ // A scrollable that does not consume the scroll gesture.
+ .scrollable(
+ state = rememberScrollableState { 0f },
+ orientation = scrollableOrientation,
+ overscrollEffect = overscrollEffect,
+ )
+ .size(layoutSize)
+ .testTag(BOX_TAG)
+ )
+ }
+ return LayoutInfo(layoutSize, touchSlop, density)
+ }
+
+ @Test
+ fun applyVerticalOffset_duringVerticalOverscroll() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+ }
+
+ @Test
+ fun applyNoOffset_duringHorizontalOverscroll() {
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ overscrollEffectOrientation = Orientation.Horizontal,
+ )
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(info.touchSlop + info.layoutSize.toPx(), 0f), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun backToZero_afterOverscroll() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+
+ rule.onRoot().performTouchInput { up() }
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun offsetOverscroll_followTheTouchPointer() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ // First gesture, drag down.
+ rule.onRoot().performTouchInput {
+ down(center)
+ // A full screen scroll.
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+
+ rule.onRoot().performTouchInput {
+ // Reduced by half.
+ moveBy(Offset(0f, -info.layoutSize.toPx() / 2), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize / 2))
+
+ rule.onRoot().performTouchInput { up() }
+ // Animate back to 0.
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Second gesture, drag up.
+ rule.onRoot().performTouchInput {
+ down(center)
+ // A full screen scroll.
+ moveBy(Offset(0f, -info.touchSlop - info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(-info.layoutSize))
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 0adb4809dd2d..9a2af640c46f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -168,12 +168,12 @@ abstract class BaseTransitionSubject<T : TransitionState.Transition>(
fun hasBouncingContent(content: ContentKey) {
val actual = actual
- if (actual !is TransitionState.HasOverscrollProperties) {
+ if (actual !is TransitionState.DirectionProperties) {
failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties"))
}
check("bouncingContent")
- .that((actual as TransitionState.HasOverscrollProperties).bouncingContent)
+ .that((actual as TransitionState.DirectionProperties).bouncingContent)
.isEqualTo(content)
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index e27f9b52153d..c8fb2cb8474f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -71,41 +71,6 @@ class LargeTopAppBarNestedScrollConnectionTest(testCase: TestCase) {
}
@Test
- fun onScrollUpAfterContentScrolled_ignoreUpEvent() {
- val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
- height = 1f
-
- // scroll down consumed by a child
- scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
-
- val offsetConsumed =
- scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
-
- // It should ignore all onPreScroll events
- assertThat(offsetConsumed).isEqualTo(Offset.Zero)
- assertThat(height).isEqualTo(1f)
- }
-
- @Test
- fun onScrollUpAfterContentReturnedToZero_consumeHeight() {
- val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
- height = 1f
-
- // scroll down consumed by a child
- scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
-
- // scroll up consumed by a child, the child is in its original position
- scrollConnection.scroll(available = Offset(0f, -1f), consumedByScroll = Offset(0f, -1f))
-
- val offsetConsumed =
- scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
-
- // It should ignore all onPreScroll events
- assertThat(offsetConsumed).isEqualTo(Offset(0f, -1f))
- assertThat(height).isEqualTo(0f)
- }
-
- @Test
fun onScrollUp_consumeDownToMin() {
val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
height = 0f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 28ea2d239b54..51483a894e1e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -39,7 +39,6 @@ import org.junit.runner.RunWith
class PriorityNestedScrollConnectionTest {
private var canStartPreScroll = false
private var canStartPostScroll = false
- private var canStartPostFling = false
private var canStopOnPreFling = true
private var isStarted = false
private var lastScroll: Float? = null
@@ -63,7 +62,6 @@ class PriorityNestedScrollConnectionTest {
orientation = Orientation.Vertical,
canStartPreScroll = { _, _, _ -> canStartPreScroll },
canStartPostScroll = { _, _, _ -> canStartPostScroll },
- canStartPostFling = { canStartPostFling },
onStart = { _ ->
isStarted = true
object : ScrollController {
@@ -239,36 +237,6 @@ class PriorityNestedScrollConnectionTest {
}
@Test
- fun receive_onPostFling() = runTest {
- canStartPostFling = true
-
- scrollConnection.onPostFling(consumed = Velocity(1f, 1f), available = Velocity(2f, 2f))
-
- assertThat(lastStop).isEqualTo(2f)
- }
-
- @Test
- fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest {
- canStartPostFling = true
-
- scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
- assertThat(isStarted).isEqualTo(false)
-
- scrollConnection.onPostScroll(
- consumed = Offset.Zero,
- available = Offset.Zero,
- source = UserInput,
- )
- assertThat(isStarted).isEqualTo(false)
-
- scrollConnection.onPreFling(available = Velocity.Zero)
- assertThat(isStarted).isEqualTo(false)
-
- scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
- assertThat(isStarted).isEqualTo(true)
- }
-
- @Test
fun handleMultipleOnPreFlingCalls() = runTest {
startPriorityModePostScroll()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
index 646cff8b944c..6015479d8e21 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
@@ -71,7 +71,7 @@ fun transition(
): TestOverlayTransition {
return object :
TestOverlayTransition(fromScene, overlay, replacedTransition),
- TransitionState.HasOverscrollProperties {
+ TransitionState.DirectionProperties {
override val isEffectivelyShown: Boolean
get() = isEffectivelyShown()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
index c342f488212a..bd2118dd8395 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
@@ -68,7 +68,7 @@ fun transition(
): TestReplaceOverlayTransition {
return object :
TestReplaceOverlayTransition(from, to, replacedTransition),
- TransitionState.HasOverscrollProperties {
+ TransitionState.DirectionProperties {
override val effectivelyShownOverlay: OverlayKey
get() = effectivelyShownOverlay()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
index d24b895c3050..1d27e3a3f191 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
@@ -62,7 +62,7 @@ fun transition(
replacedTransition: Transition? = null,
): TestSceneTransition {
return object :
- TestSceneTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
+ TestSceneTransition(from, to, replacedTransition), TransitionState.DirectionProperties {
override val currentScene: SceneKey
get() = current()
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 300a3e204582..ad9eba841c86 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
@@ -106,10 +106,8 @@ class DefaultClockController(
largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction)
smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction)
- val theme = ThemeConfig(isDarkTheme, settings?.seedColor)
- largeClock.events.onThemeChanged(theme)
- smallClock.events.onThemeChanged(theme)
-
+ largeClock.events.onThemeChanged(largeClock.theme.copy(isDarkTheme = isDarkTheme))
+ smallClock.events.onThemeChanged(smallClock.theme.copy(isDarkTheme = isDarkTheme))
events.onTimeZoneChanged(TimeZone.getDefault())
smallClock.events.onTimeTick()
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 c7a3f63e92e7..7f01fd7c87ac 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
@@ -24,7 +24,6 @@ import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-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.view.FlexClockView
@@ -107,18 +106,16 @@ class FlexClockController(
}
override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) {
- val theme = ThemeConfig(isDarkTheme, clockCtx.settings.seedColor)
events.onFontAxesChanged(clockCtx.settings.axes)
-
smallClock.run {
- events.onThemeChanged(theme)
+ events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
}
largeClock.run {
- events.onThemeChanged(theme)
+ events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
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 a8890e6aa934..21d41ae744a7 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
@@ -131,6 +131,7 @@ class FlexClockFaceController(
}
override fun onThemeChanged(theme: ThemeConfig) {
+ this@FlexClockFaceController.theme = theme
layerController.faceEvents.onThemeChanged(theme)
}
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index 00b0c44c2077..b0963d352990 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -26857,8 +26857,8 @@
<issue
id="Overdraw"
- message="Possible overdraw: Root element paints background `?androidprv:attr/materialColorOnSurfaceVariant` with a theme that also paints a background (inferred theme is `@style/Theme.SystemUI`)"
- errorLine1=" android:background=&quot;?androidprv:attr/materialColorOnSurfaceVariant&quot; />"
+ message="Possible overdraw: Root element paints background `@androidprv:color/materialColorOnSurfaceVariant` with a theme that also paints a background (inferred theme is `@style/Theme.SystemUI`)"
+ errorLine1=" android:background=&quot;@androidprv:color/materialColorOnSurfaceVariant&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="frameworks/base/packages/SystemUI/res/layout/notification_children_divider.xml"
@@ -26868,8 +26868,8 @@
<issue
id="Overdraw"
- message="Possible overdraw: Root element paints background `?androidprv:attr/materialColorSurfaceContainerHigh` with a theme that also paints a background (inferred theme is `@style/Theme.SystemUI`)"
- errorLine1=" android:background=&quot;?androidprv:attr/materialColorSurfaceContainerHigh&quot;"
+ message="Possible overdraw: Root element paints background `@androidprv:color/materialColorSurfaceContainerHigh` with a theme that also paints a background (inferred theme is `@style/Theme.SystemUI`)"
+ errorLine1=" android:background=&quot;@androidprv:color/materialColorSurfaceContainerHigh&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml"
@@ -32784,984 +32784,4 @@
column="23"/>
</issue>
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt"
- line="39"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @NonNull Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java"
- line="300"
- column="30"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" Context context, DeviceConfigProxy proxy) {"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java"
- line="75"
- column="21"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" public AuthController(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
- line="716"
- column="35"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @NonNull WindowManager windowManager,"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
- line="721"
- column="36"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val sysuiContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
- line="72"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val configurationController: ConfigurationController,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
- line="74"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main protected val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java"
- line="46"
- column="21"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main Resources resources,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java"
- line="52"
- column="29"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" public BiometricNotificationService(@NonNull Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java"
- line="148"
- column="58"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt"
- line="62"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt"
- line="37"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" c: Context,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt"
- line="61"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt"
- line="52"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt"
- line="30"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="class CustomTileStatePersisterImpl @Inject constructor(context: Context) :"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt"
- line="74"
- column="56"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt"
- line="77"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt"
- line="68"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt"
- line="38"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt"
- line="37"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt"
- line="42"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt"
- line="95"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt"
- line="142"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt"
- line="44"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @NonNull final Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
- line="186"
- column="36"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" ConfigurationController configurationController,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
- line="192"
- column="37"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="class IconBuilder @Inject constructor(private val context: Context) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt"
- line="27"
- column="39"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt"
- line="31"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val windowManager: WindowManager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
- line="39"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
- line="40"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt"
- line="41"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val layoutInflater: LayoutInflater"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt"
- line="29"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt"
- line="38"
- column="58"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt"
- line="38"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt"
- line="42"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" public NotificationGutsManager(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java"
- line="137"
- column="44"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt"
- line="47"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt"
- line="53"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(val context: Context) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt"
- line="27"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" ConfigurationController configurationController,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java"
- line="737"
- column="37"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt"
- line="63"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" Builder(@Main Resources resources, ViewConfiguration viewConfiguration,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java"
- line="563"
- column="33"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" public PackageManagerAdapter(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java"
- line="45"
- column="42"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt"
- line="36"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt"
- line="74"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt"
- line="41"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt"
- line="82"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt"
- line="32"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt"
- line="36"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" Factory(Context context, QSCustomizerController qsCustomizerController) {"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java"
- line="99"
- column="25"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt"
- line="32"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt"
- line="36"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt"
- line="47"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt"
- line="36"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt"
- line="51"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt"
- line="33"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val layoutInflater: LayoutInflater,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt"
- line="43"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" context: Context"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt"
- line="35"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt"
- line="63"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt"
- line="53"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
- line="51"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" windowManager: WindowManager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
- line="53"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val applicationContext: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
- line="60"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
- line="65"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt"
- line="91"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(context: Context, val shadeViewController: ShadeViewController) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt"
- line="30"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/StockTilesRepository.kt"
- line="31"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val context: Context"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt"
- line="33"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt"
- line="95"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt"
- line="33"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Application private val context: Context,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt"
- line="49"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" private val configurationController: ConfigurationController,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt"
- line="43"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt"
- line="37"
- column="13"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @Main private val resources: Resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt"
- line="36"
- column="5"/>
- </issue>
-
- <issue
- id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationInteractor, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationInteractor is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
- errorLine1=" @GlobalConfig configurationInteractor: ConfigurationInteractor,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
- line="43"
- column="5"/>
- </issue>
-
-
</issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
deleted file mode 100644
index ce57fe256798..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.view.View.INVISIBLE;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.log.LogBuffer;
-import com.android.systemui.plugins.clocks.ClockAnimations;
-import com.android.systemui.plugins.clocks.ClockController;
-import com.android.systemui.plugins.clocks.ClockEvents;
-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.ClockTickRate;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.shared.clocks.AnimatableClockView;
-import com.android.systemui.shared.clocks.ClockRegistry;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
-import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
-
- @Mock
- protected KeyguardClockSwitch mView;
- @Mock
- protected StatusBarStateController mStatusBarStateController;
- @Mock
- protected ClockRegistry mClockRegistry;
- @Mock
- KeyguardSliceViewController mKeyguardSliceViewController;
- @Mock
- LockscreenSmartspaceController mSmartspaceController;
-
- @Mock
- Resources mResources;
- @Mock
- KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- @Mock
- protected ClockController mClockController;
- @Mock
- protected ClockFaceController mLargeClockController;
- @Mock
- protected ClockFaceController mSmallClockController;
- @Mock
- protected ClockAnimations mClockAnimations;
- @Mock
- protected ClockEvents mClockEvents;
- @Mock
- protected ClockFaceEvents mClockFaceEvents;
- @Mock
- DumpManager mDumpManager;
- @Mock
- ClockEventController mClockEventController;
-
- @Mock
- protected NotificationIconContainer mNotificationIcons;
- @Mock
- protected AnimatableClockView mSmallClockView;
- @Mock
- protected AnimatableClockView mLargeClockView;
- @Mock
- protected FrameLayout mSmallClockFrame;
- @Mock
- protected FrameLayout mLargeClockFrame;
- @Mock
- protected SecureSettings mSecureSettings;
- @Mock
- protected LogBuffer mLogBuffer;
-
- @Mock
- protected KeyguardClockInteractor mKeyguardClockInteractor;
-
- protected final View mFakeDateView = (View) (new ViewGroup(mContext) {
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {}
- });
- protected final View mFakeWeatherView = new View(mContext);
- protected final View mFakeSmartspaceView = new View(mContext);
-
- protected KeyguardClockSwitchController mController;
- protected View mSliceView;
- protected LinearLayout mStatusArea;
- protected FakeExecutor mExecutor;
- protected FakeFeatureFlags mFakeFeatureFlags;
- @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mFakeDateView.setTag(R.id.tag_smartspace_view, new Object());
- mFakeWeatherView.setTag(R.id.tag_smartspace_view, new Object());
- mFakeSmartspaceView.setTag(R.id.tag_smartspace_view, new Object());
-
- when(mView.findViewById(R.id.left_aligned_notification_icon_container))
- .thenReturn(mNotificationIcons);
- when(mNotificationIcons.getLayoutParams()).thenReturn(
- mock(RelativeLayout.LayoutParams.class));
- when(mView.getContext()).thenReturn(getContext());
- when(mView.getResources()).thenReturn(mResources);
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
- .thenReturn(100);
- when(mResources.getDimensionPixelSize(com.android.systemui.customization.R.dimen.keyguard_large_clock_top_margin))
- .thenReturn(-200);
- when(mResources.getInteger(com.android.internal.R.integer.config_doublelineClockDefault))
- .thenReturn(1);
- when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
- .thenReturn(INVISIBLE);
-
- when(mView
- .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large))
- .thenReturn(mLargeClockFrame);
- when(mView
- .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view))
- .thenReturn(mSmallClockFrame);
- when(mSmallClockView.getContext()).thenReturn(getContext());
- when(mLargeClockView.getContext()).thenReturn(getContext());
-
- when(mView.isAttachedToWindow()).thenReturn(true);
- when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView);
- when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView);
- when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
- mExecutor = new FakeExecutor(new FakeSystemClock());
- mFakeFeatureFlags = new FakeFeatureFlags();
- mController = new KeyguardClockSwitchController(
- mView,
- mStatusBarStateController,
- mClockRegistry,
- mKeyguardSliceViewController,
- mSmartspaceController,
- mock(NotificationIconContainerAlwaysOnDisplayViewBinder.class),
- mKeyguardUnlockAnimationController,
- mSecureSettings,
- mExecutor,
- mExecutor,
- mDumpManager,
- mClockEventController,
- mLogBuffer,
- KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
- mKeyguardClockInteractor,
- mock(InWindowLauncherUnlockAnimationManager.class)
- );
-
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
- when(mLargeClockController.getView()).thenReturn(mLargeClockView);
- when(mSmallClockController.getView()).thenReturn(mSmallClockView);
- when(mClockController.getLargeClock()).thenReturn(mLargeClockController);
- when(mClockController.getSmallClock()).thenReturn(mSmallClockController);
- when(mClockController.getEvents()).thenReturn(mClockEvents);
- when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
- when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
- when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations);
- when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations);
- when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
- when(mClockEventController.getClock()).thenReturn(mClockController);
- when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
- when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
-
- mSliceView = new View(getContext());
- when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
- mStatusArea = new LinearLayout(getContext());
- when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
- }
-
- private void removeView(View v) {
- ViewGroup group = ((ViewGroup) v.getParent());
- if (group != null) {
- group.removeView(v);
- }
- }
-
- protected void init() {
- mController.init();
-
- verify(mView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
- mAttachCaptor.getValue().onViewAttachedToWindow(mView);
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
deleted file mode 100644
index 892375d002c1..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ /dev/null
@@ -1,278 +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.keyguard;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.database.ContentObserver;
-import android.os.UserHandle;
-import android.platform.test.annotations.DisableFlags;
-import android.provider.Settings;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Flags;
-import com.android.systemui.plugins.clocks.ClockFaceConfig;
-import com.android.systemui.plugins.clocks.ClockTickRate;
-import com.android.systemui.shared.clocks.ClockRegistry;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.verification.VerificationMode;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest {
- @Test
- public void testInit_viewAlreadyAttached() {
- mController.init();
-
- verifyAttachment(times(1));
- }
-
- @Test
- public void testInit_viewNotYetAttached() {
- ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
- when(mView.isAttachedToWindow()).thenReturn(false);
- mController.init();
- verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
-
- verifyAttachment(never());
-
- listenerArgumentCaptor.getValue().onViewAttachedToWindow(mView);
-
- verifyAttachment(times(1));
- }
-
- @Test
- public void testInitSubControllers() {
- mController.init();
- verify(mKeyguardSliceViewController).init();
- }
-
- @Test
- public void testInit_viewDetached() {
- ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
- mController.init();
- verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
-
- verifyAttachment(times(1));
-
- listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
- verify(mClockEventController).unregisterListeners();
- }
-
- @Test
- public void testPluginPassesStatusBarState() {
- ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
-
- mController.init();
- verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
-
- listenerArgumentCaptor.getValue().onCurrentClockChanged();
- verify(mView, times(2)).setClock(mClockController, StatusBarState.SHADE);
- verify(mClockEventController, times(2)).setClock(mClockController);
- }
-
- @Test
- public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- mController.init();
-
- assertEquals(View.GONE, mSliceView.getVisibility());
- }
-
- @Test
- public void onLocaleListChangedRebuildsSmartspaceView() {
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- mController.init();
-
- mController.onLocaleListChanged();
- // Should be called once on initial setup, then once again for locale change
- verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
- }
-
- @Test
- public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
- mController.init();
-
- mController.onLocaleListChanged();
- // Should be called once on initial setup, then once again for locale change
- verify(mSmartspaceController, times(2)).buildAndConnectDateView(mView);
- verify(mSmartspaceController, times(2)).buildAndConnectWeatherView(mView);
- verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
- }
-
- @Test
- public void testSmartspaceDisabledShowsKeyguardStatusArea() {
- when(mSmartspaceController.isEnabled()).thenReturn(false);
- mController.init();
-
- assertEquals(View.VISIBLE, mSliceView.getVisibility());
- }
-
- @Test
- public void testRefresh() {
- mController.refresh();
-
- verify(mSmartspaceController).requestSmartspaceUpdate();
- }
-
- @Test
- public void testChangeToDoubleLineClockSetsSmallClock() {
- when(mSecureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1,
- UserHandle.USER_CURRENT))
- .thenReturn(0);
- ArgumentCaptor<ContentObserver> observerCaptor =
- ArgumentCaptor.forClass(ContentObserver.class);
- mController.init();
- mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUserSync(
- eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
- anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
- ContentObserver observer = observerCaptor.getValue();
- mExecutor.runAllReady();
-
- // When a settings change has occurred to the small clock, make sure the view is adjusted
- reset(mView);
- when(mView.getResources()).thenReturn(mResources);
- observer.onChange(true);
- mExecutor.runAllReady();
- verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
- }
-
- @Test
- public void testGetClock_ForwardsToClock() {
- assertEquals(mClockController, mController.getClock());
- }
-
- @Test
- public void testGetLargeClockBottom_returnsExpectedValue() {
- when(mLargeClockFrame.getVisibility()).thenReturn(View.VISIBLE);
- when(mLargeClockFrame.getHeight()).thenReturn(100);
- when(mSmallClockFrame.getHeight()).thenReturn(50);
- when(mLargeClockView.getHeight()).thenReturn(40);
- when(mSmallClockView.getHeight()).thenReturn(20);
- mController.init();
-
- assertEquals(170, mController.getClockBottom(1000));
- }
-
- @Test
- public void testGetSmallLargeClockBottom_returnsExpectedValue() {
- when(mLargeClockFrame.getVisibility()).thenReturn(View.GONE);
- when(mLargeClockFrame.getHeight()).thenReturn(100);
- when(mSmallClockFrame.getHeight()).thenReturn(50);
- when(mLargeClockView.getHeight()).thenReturn(40);
- when(mSmallClockView.getHeight()).thenReturn(20);
- mController.init();
-
- assertEquals(1120, mController.getClockBottom(1000));
- }
-
- @Test
- public void testGetClockBottom_nullClock_returnsZero() {
- when(mClockEventController.getClock()).thenReturn(null);
- assertEquals(0, mController.getClockBottom(10));
- }
-
- @Test
- public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() {
- when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
- ArgumentCaptor<ContentObserver> observerCaptor =
- ArgumentCaptor.forClass(ContentObserver.class);
- mController.init();
- mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUserSync(
- eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
- observerCaptor.capture(), eq(UserHandle.USER_ALL));
- ContentObserver observer = observerCaptor.getValue();
- mExecutor.runAllReady();
- // When a settings change has occurred, check that view is visible.
- observer.onChange(true);
- mExecutor.runAllReady();
- assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
- }
-
- @Test
- public void testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility() {
- ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
- when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
- mController.init();
- mExecutor.runAllReady();
- assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
-
- when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
- when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
- verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
- listenerArgumentCaptor.getValue().onCurrentClockChanged();
-
- mExecutor.runAllReady();
- assertEquals(View.INVISIBLE, mFakeDateView.getVisibility());
- }
-
- @Test
- public void testGetClock_nullClock_returnsNull() {
- when(mClockEventController.getClock()).thenReturn(null);
- assertNull(mController.getClock());
- }
-
- private void verifyAttachment(VerificationMode times) {
- verify(mClockRegistry, times).registerClockChangeListener(
- any(ClockRegistry.ClockChangeListener.class));
- verify(mClockEventController, times).registerListeners(mView);
- }
-
- @Test
- public void testSplitShadeEnabledSetToSmartspaceController() {
- mController.setSplitShadeEnabled(true);
- verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
- verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
- }
-
- @Test
- public void testSplitShadeDisabledSetToSmartspaceController() {
- mController.setSplitShadeEnabled(false);
- verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
- verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
deleted file mode 100644
index 4ed5fd0a6e71..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ /dev/null
@@ -1,273 +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.keyguard;
-
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
-import static com.android.keyguard.KeyguardClockSwitch.LARGE;
-import static com.android.keyguard.KeyguardClockSwitch.SMALL;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.TestCase.assertEquals;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.platform.test.annotations.DisableFlags;
-import android.testing.TestableLooper.RunWithLooper;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-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.plugins.clocks.ClockController;
-import com.android.systemui.plugins.clocks.ClockFaceController;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-// Need to run on the main thread because KeyguardSliceView$Row init checks for
-// the main thread before acquiring a wake lock. This class is constructed when
-// the keyguard_clock_switch layout is inflated.
-@RunWithLooper(setAsMainLooper = true)
-@DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-public class KeyguardClockSwitchTest extends SysuiTestCase {
- @Mock
- ViewGroup mMockKeyguardSliceView;
-
- @Mock
- ClockController mClock;
-
- @Mock
- ClockFaceController mSmallClock;
-
- @Mock
- ClockFaceController mLargeClock;
-
- private FrameLayout mSmallClockFrame;
- private FrameLayout mLargeClockFrame;
- private KeyguardStatusAreaView mStatusArea;
-
- KeyguardClockSwitch mKeyguardClockSwitch;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
- when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
- .thenReturn(mMockKeyguardSliceView);
-
- when(mClock.getSmallClock()).thenReturn(mSmallClock);
- when(mClock.getLargeClock()).thenReturn(mLargeClock);
-
- when(mSmallClock.getView()).thenReturn(new TextView(getContext()));
- when(mLargeClock.getView()).thenReturn(new TextView(getContext()));
-
- LayoutInflater layoutInflater = LayoutInflater.from(getContext());
- layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
- @Override
- public View onCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return onCreateView(name, context, attrs);
- }
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
- return mMockKeyguardSliceView;
- }
- return null;
- }
- });
- mKeyguardClockSwitch =
- (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
- mSmallClockFrame = mKeyguardClockSwitch
- .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
- mLargeClockFrame = mKeyguardClockSwitch
- .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
- mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area);
- mKeyguardClockSwitch.mChildrenAreLaidOut = true;
- }
-
- @Test
- public void noPluginConnected_showNothing() {
- mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
- assertEquals(mLargeClockFrame.getChildCount(), 0);
- assertEquals(mSmallClockFrame.getChildCount(), 0);
- }
-
- @Test
- public void pluginConnectedThenDisconnected_showNothing() {
- mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
- assertEquals(mLargeClockFrame.getChildCount(), 1);
- assertEquals(mSmallClockFrame.getChildCount(), 1);
-
- mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
- assertEquals(mLargeClockFrame.getChildCount(), 0);
- assertEquals(mSmallClockFrame.getChildCount(), 0);
- }
-
- @Test
- public void onPluginConnected_showClock() {
- mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
-
- assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame);
- assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame);
- }
-
- @Test
- public void onPluginConnected_showSecondPluginClock() {
- // GIVEN a plugin has already connected
- ClockController otherClock = mock(ClockController.class);
- ClockFaceController smallClock = mock(ClockFaceController.class);
- ClockFaceController largeClock = mock(ClockFaceController.class);
- when(otherClock.getSmallClock()).thenReturn(smallClock);
- when(otherClock.getLargeClock()).thenReturn(largeClock);
- when(smallClock.getView()).thenReturn(new TextView(getContext()));
- when(largeClock.getView()).thenReturn(new TextView(getContext()));
- mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
- mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
-
- // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
- assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame);
- assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame);
- assertThat(mClock.getSmallClock().getView().getParent()).isNull();
- assertThat(mClock.getLargeClock().getView().getParent()).isNull();
- }
-
- @Test
- public void onPluginDisconnected_secondOfTwoDisconnected() {
- // GIVEN two plugins are connected
- ClockController otherClock = mock(ClockController.class);
- ClockFaceController smallClock = mock(ClockFaceController.class);
- ClockFaceController largeClock = mock(ClockFaceController.class);
- when(otherClock.getSmallClock()).thenReturn(smallClock);
- when(otherClock.getLargeClock()).thenReturn(largeClock);
- when(smallClock.getView()).thenReturn(new TextView(getContext()));
- when(largeClock.getView()).thenReturn(new TextView(getContext()));
- mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
- mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
- // WHEN the second plugin is disconnected
- mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
- // THEN nothing should be shown
- assertThat(otherClock.getSmallClock().getView().getParent()).isNull();
- assertThat(otherClock.getLargeClock().getView().getParent()).isNull();
- assertThat(mClock.getSmallClock().getView().getParent()).isNull();
- assertThat(mClock.getLargeClock().getView().getParent()).isNull();
- }
-
- @Test
- public void switchingToBigClockWithAnimation_makesSmallClockDisappear() {
- mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true);
-
- mKeyguardClockSwitch.mClockInAnim.end();
- mKeyguardClockSwitch.mClockOutAnim.end();
- mKeyguardClockSwitch.mStatusAreaAnim.end();
-
- assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
- assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
- assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
- assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
- }
-
- @Test
- public void switchingToBigClockNoAnimation_makesSmallClockDisappear() {
- mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false);
-
- assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
- assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
- assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
- assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
- }
-
- @Test
- public void switchingToSmallClockWithAnimation_makesBigClockDisappear() {
- mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true);
-
- mKeyguardClockSwitch.mClockInAnim.end();
- mKeyguardClockSwitch.mClockOutAnim.end();
- mKeyguardClockSwitch.mStatusAreaAnim.end();
-
- assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
- assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
- // only big clock is removed at switch
- assertThat(mLargeClockFrame.getParent()).isNull();
- assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
- assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
- }
-
- @Test
- public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
- mKeyguardClockSwitch.switchToClock(SMALL, false);
-
- assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
- assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
- // only big clock is removed at switch
- assertThat(mLargeClockFrame.getParent()).isNull();
- assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
- assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
- }
-
- @Test
- public void switchingToSmallClockAnimation_resetsStatusArea() {
- mKeyguardClockSwitch.switchToClock(SMALL, true);
-
- mKeyguardClockSwitch.mClockInAnim.end();
- mKeyguardClockSwitch.mClockOutAnim.end();
- mKeyguardClockSwitch.mStatusAreaAnim.end();
-
- assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
- assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
- assertThat(mStatusArea.getScaleX()).isEqualTo(1);
- assertThat(mStatusArea.getScaleY()).isEqualTo(1);
- }
-
- @Test
- public void switchingToSmallClockNoAnimation_resetsStatusArea() {
- mKeyguardClockSwitch.switchToClock(SMALL, false);
-
- assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
- assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
- assertThat(mStatusArea.getScaleX()).isEqualTo(1);
- assertThat(mStatusArea.getScaleY()).isEqualTo(1);
- }
-
-
- @Test
- public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse();
- }
-}
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 fa8cdcc4ce2b..80de087971c5 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
@@ -24,6 +24,7 @@ import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -39,6 +40,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -54,11 +56,15 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mockSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+ mockSecureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 7e4b6f913770..24f3a29e64ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -16,11 +16,16 @@
package com.android.systemui.accessibility.floatingmenu;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.content.res.Configuration;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -28,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.settings.SecureSettings;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -36,6 +42,8 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
/** Tests for {@link MenuInfoRepository}. */
@@ -46,16 +54,30 @@ public class MenuInfoRepositoryTest extends SysuiTestCase {
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
+ private AccessibilityManager mAccessibilityManager;
+
+ @Mock
private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
@Mock
private SecureSettings mSecureSettings;
private MenuInfoRepository mMenuInfoRepository;
+ private final List<String> mShortcutTargets = new ArrayList<>();
@Before
public void setUp() {
- mMenuInfoRepository = new MenuInfoRepository(mContext, mMockSettingsContentsChanged,
- mSecureSettings);
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+ mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+ doReturn(mShortcutTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
+ anyInt());
+
+ mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
+ mMockSettingsContentsChanged, mSecureSettings);
+ }
+
+ @After
+ public void tearDown() {
+ mShortcutTargets.clear();
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 1f48bec97b2d..157cccc3d62f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -83,7 +83,8 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+ mSecureSettings);
final int halfScreenHeight =
stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
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 f7b81cc49f0b..46f076a75116 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
@@ -33,6 +33,7 @@ import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.RecyclerView;
@@ -52,6 +53,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -78,11 +80,15 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+
@Before
public void setUp() throws Exception {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+ secureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
windowManager);
mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index c1708d175224..ee8ce17cecd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -32,6 +32,7 @@ import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -48,6 +49,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -66,6 +68,9 @@ public class MenuViewTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+
private SysuiTestableContext mSpyContext;
@Before
@@ -84,7 +89,8 @@ public class MenuViewTest extends SysuiTestCase {
doNothing().when(mSpyContext).startActivity(any());
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+ secureSettings);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
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 9d471f45a293..ad12c61ab5d1 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
@@ -139,13 +139,11 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
private ActivityInfo mActivityInfo;
@Mock
private Drawable mDrawable;
- @Mock
- private HearingDevicesPresetsController mPresetsController;
+
private SystemUIDialog mDialog;
private SystemUIDialog.Factory mDialogFactory;
private HearingDevicesDialogDelegate mDialogDelegate;
private TestableLooper mTestableLooper;
- private final List<CachedBluetoothDevice> mDevices = new ArrayList<>();
@Before
public void setUp() {
@@ -155,7 +153,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
- when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(List.of(mCachedDevice));
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
@@ -163,6 +161,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
when(mCachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedHapClientDevice()).thenReturn(true);
@@ -170,12 +169,11 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
mContext.setMockPackageManager(mPackageManager);
- mDevices.add(mCachedDevice);
}
@Test
public void clickPairNewDeviceButton_intentActionMatch() {
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithPairNewDeviceButton();
mDialog.show();
getPairNewDeviceButton(mDialog).performClick();
@@ -191,7 +189,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@Test
public void onDeviceItemGearClicked_intentActionMatch() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialogDelegate.onDeviceItemGearClicked(mHearingDeviceItem, new View(mContext));
@@ -206,7 +204,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@Test
public void onDeviceItemOnClicked_connectedDevice_disconnect() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
when(mHearingDeviceItem.getType()).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE);
mDialogDelegate.onDeviceItemClicked(mHearingDeviceItem, new View(mContext));
@@ -222,7 +220,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(0);
@@ -237,7 +235,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(1);
@@ -247,9 +245,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
throws PackageManager.NameNotFoundException {
- when(mPackageManager.queryIntentActivities(
- eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
- List.of(new ResolveInfo()));
+ when(mPackageManager.queryIntentActivities(eq(LIVE_CAPTION_INTENT), anyInt()))
+ .thenReturn(List.of(new ResolveInfo()));
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName,
new String[]{TEST_PKG + "/" + TEST_CLS});
@@ -260,18 +257,18 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
when(mDrawable.mutate()).thenReturn(mDrawable);
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(2);
}
@Test
- public void showDialog_noPreset_presetGone() {
- when(mPresetsController.getAllPresetInfo()).thenReturn(new ArrayList<>());
- when(mPresetsController.getActivePresetIndex()).thenReturn(PRESET_INDEX_UNAVAILABLE);
+ public void showDialog_noPreset_presetLayoutGone() {
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(new ArrayList<>());
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(PRESET_INDEX_UNAVAILABLE);
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
ViewGroup presetLayout = getPresetLayout(mDialog);
@@ -281,11 +278,12 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@Test
public void showDialog_presetExist_presetSelected() {
BluetoothHapPresetInfo info = getTestPresetInfo();
- when(mPresetsController.getAllPresetInfo()).thenReturn(List.of(info));
- when(mPresetsController.getActivePresetIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
+ mTestableLooper.processAllMessages();
ViewGroup presetLayout = getPresetLayout(mDialog);
assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -295,48 +293,32 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@Test
public void onActiveDeviceChanged_presetExist_presetSelected() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
BluetoothHapPresetInfo info = getTestPresetInfo();
- when(mPresetsController.getAllPresetInfo()).thenReturn(List.of(info));
- when(mPresetsController.getActivePresetIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
+
+ Spinner spinner = getPresetSpinner(mDialog);
+ assertThat(spinner.getSelectedItemPosition()).isEqualTo(-1);
mDialogDelegate.onActiveDeviceChanged(mCachedDevice, BluetoothProfile.LE_AUDIO);
mTestableLooper.processAllMessages();
ViewGroup presetLayout = getPresetLayout(mDialog);
assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE);
- Spinner spinner = getPresetSpinner(mDialog);
assertThat(spinner.getSelectedItemPosition()).isEqualTo(0);
}
+ private void setUpDeviceDialogWithPairNewDeviceButton() {
+ setUpDeviceDialog(/* showPairNewDevice= */ true);
+ }
-
- private void setUpPairNewDeviceDialog() {
- mDialogFactory = new SystemUIDialog.Factory(
- mContext,
- mSystemUIDialogManager,
- mSysUiState,
- getFakeBroadcastDispatcher(),
- mDialogTransitionAnimator
- );
- mDialogDelegate = new HearingDevicesDialogDelegate(
- mContext,
- true,
- TEST_LAUNCH_SOURCE_ID,
- mDialogFactory,
- mActivityStarter,
- mDialogTransitionAnimator,
- mLocalBluetoothManager,
- new Handler(mTestableLooper.getLooper()),
- mAudioManager,
- mUiEventLogger
- );
-
- mDialog = mDialogDelegate.createDialog();
+ private void setUpDeviceDialogWithoutPairNewDeviceButton() {
+ setUpDeviceDialog(/* showPairNewDevice= */ false);
}
- private void setUpDeviceListDialog() {
+ private void setUpDeviceDialog(boolean showPairNewDevice) {
mDialogFactory = new SystemUIDialog.Factory(
mContext,
mSystemUIDialogManager,
@@ -345,8 +327,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mDialogTransitionAnimator
);
mDialogDelegate = new HearingDevicesDialogDelegate(
- mContext,
- false,
+ showPairNewDevice,
TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
@@ -356,15 +337,14 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mAudioManager,
mUiEventLogger
);
-
mDialog = mDialogDelegate.createDialog();
- mDialogDelegate.setHearingDevicesPresetsController(mPresetsController);
}
private BluetoothHapPresetInfo getTestPresetInfo() {
BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
when(info.getName()).thenReturn(TEST_PRESET_NAME);
when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(info.isAvailable()).thenReturn(true);
return info;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
index 2ac5d105ba99..c9779c90c9aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
@@ -21,10 +21,10 @@ 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.anyList;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.VerificationKt.never;
import static java.util.Collections.emptyList;
@@ -39,7 +39,6 @@ import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.SysuiTestCase;
@@ -53,6 +52,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/** Tests for {@link HearingDevicesPresetsController}. */
@@ -62,6 +62,7 @@ import java.util.concurrent.Executor;
public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
private static final int TEST_PRESET_INDEX = 1;
+ private static final int TEST_UPDATED_PRESET_INDEX = 2;
private static final String TEST_PRESET_NAME = "test_preset";
private static final int TEST_HAP_GROUP_ID = 1;
private static final int TEST_REASON = 1024;
@@ -74,14 +75,13 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
@Mock
private HapClientProfile mHapClientProfile;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
+ private CachedBluetoothDevice mCachedDevice;
@Mock
- private CachedBluetoothDevice mSubCachedBluetoothDevice;
+ private CachedBluetoothDevice mCachedMemberDevice;
@Mock
- private BluetoothDevice mBluetoothDevice;
+ private BluetoothDevice mDevice;
@Mock
- private BluetoothDevice mSubBluetoothDevice;
-
+ private BluetoothDevice mMemberDevice;
@Mock
private HearingDevicesPresetsController.PresetCallback mCallback;
@@ -91,15 +91,19 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
public void setUp() {
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
when(mHapClientProfile.isProfileReady()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
- when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedMemberDevice));
+ when(mCachedMemberDevice.getDevice()).thenReturn(mMemberDevice);
mController = new HearingDevicesPresetsController(mProfileManager, mCallback);
+ mController.setDevice(mCachedDevice);
}
@Test
public void onServiceConnected_callExpectedCallback() {
+ preparePresetInfo(/* isValid= */ true);
+
mController.onServiceConnected();
verify(mHapClientProfile).registerCallback(any(Executor.class),
@@ -108,115 +112,129 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
}
@Test
- public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() {
- when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList());
- mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void setDevice_nonHapDevice_getEmptyListAndInvalidActiveIndex() {
+ when(mCachedDevice.getProfiles()).thenReturn(emptyList());
+ preparePresetInfo(/* isValid= */ true);
+
+ mController.setDevice(mCachedDevice);
assertThat(mController.getAllPresetInfo()).isEmpty();
+ assertThat(mController.getActivePresetIndex()).isEqualTo(
+ BluetoothHapClient.PRESET_INDEX_UNAVAILABLE);
}
@Test
- public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void refreshPresetInfo_containsOnlyNotAvailablePresetInfo_getEmptyList() {
+ preparePresetInfo(/* isValid= */ false);
+
+ mController.refreshPresetInfo();
assertThat(mController.getAllPresetInfo()).isEmpty();
}
@Test
- public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void refreshPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo);
+ mController.refreshPresetInfo();
+
+ List<BluetoothHapPresetInfo> presetInfos = mController.getAllPresetInfo();
+ assertThat(presetInfos.size()).isEqualTo(1);
+ assertThat(presetInfos).contains(infos.getFirst());
}
@Test
- public void getActivePresetIndex_getExpectedIndex() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ public void refreshPresetInfo_getExpectedIndex() {
+ preparePresetInfo(/* isValid= */ true);
+
+ mController.refreshPresetInfo();
assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX);
}
@Test
- public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ public void refreshPresetInfo_callbackIsCalledWhenNeeded() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
+
+ mController.refreshPresetInfo();
+
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
+
+ Mockito.reset(mCallback);
+ mController.refreshPresetInfo();
+
+ verify(mCallback, never()).onPresetInfoUpdated(anyList(), anyInt());
+
+ Mockito.reset(mCallback);
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_UPDATED_PRESET_INDEX);
+ mController.refreshPresetInfo();
+
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_UPDATED_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetSelected_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON);
+ mController.onPresetSelected(mDevice, TEST_PRESET_INDEX, TEST_REASON);
- verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX));
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
}
@Test
- public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ public void onPresetInfoChanged_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON);
+ mController.onPresetInfoChanged(mDevice, infos, TEST_REASON);
- verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX);
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
}
@Test
public void onPresetSelectionFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
- mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON);
+ mController.onPresetSelectionFailed(mDevice, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
public void onSetPresetNameFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
- mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON);
+ mController.onSetPresetNameFailed(mDevice, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
- public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
+ public void onPresetSelectionForGroupFailed_callSelectPresetIndependently() {
mController.selectPreset(TEST_PRESET_INDEX);
Mockito.reset(mHapClientProfile);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
-
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
}
@Test
public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
+ public void registerHapCallback_profileNotReady_addServiceListener() {
+ when(mHapClientProfile.isProfileReady()).thenReturn(false);
+
+ mController.registerHapCallback();
+
+ verify(mProfileManager).addServiceListener(mController);
+ verify(mHapClientProfile, never()).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
public void registerHapCallback_callHapRegisterCallback() {
mController.registerHapCallback();
@@ -233,9 +251,8 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
@Test
public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.selectPreset(TEST_PRESET_INDEX);
@@ -243,28 +260,34 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
}
@Test
- public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(
+ public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndependently() {
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
mController.selectPreset(TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
}
@Test
- public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndependently() {
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.selectPreset(TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
+ }
+
+ private List<BluetoothHapPresetInfo> preparePresetInfo(boolean isValid) {
+ BluetoothHapPresetInfo info = getHapPresetInfo(isValid);
+ List<BluetoothHapPresetInfo> infos = List.of(info);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(infos);
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
+ return infos;
}
private BluetoothHapPresetInfo getHapPresetInfo(boolean available) {
@@ -274,12 +297,4 @@ public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
when(info.isAvailable()).thenReturn(available);
return info;
}
-
- private void setValidHearingDeviceSupportHap() {
- LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class);
- List<LocalBluetoothProfile> profiles = List.of(hapClientProfile);
- when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles);
-
- mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
- }
}
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 2817f5573865..acc97a9f8642 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -182,6 +183,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
+ private ArgumentCaptor<KeyguardManager.KeyguardLockedStateListener> mKeyguardLockedStateCaptor;
+ @Captor
private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
@Captor
private ArgumentCaptor<Integer> mModalityCaptor;
@@ -192,6 +195,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private VibratorHelper mVibratorHelper;
@Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
private MSDLPlayer mMSDLPlayer;
private TestableContext mContextSpy;
@@ -272,6 +277,9 @@ public class AuthControllerTest extends SysuiTestCase {
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
+ verify(mKeyguardManager).addKeyguardLockedStateListener(any(),
+ mKeyguardLockedStateCaptor.capture());
+
// Ensures that the operations posted on the handler get executed.
waitForIdleSync();
}
@@ -977,6 +985,18 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
+ public void testCloseDialog_whenDeviceLocks() throws Exception {
+ showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
+
+ mKeyguardLockedStateCaptor.getValue().onKeyguardLockedStateChanged(
+ true /* isKeyguardLocked */);
+
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
+ }
+
+ @Test
public void testShowDialog_whenOwnerNotInForeground() {
PromptInfo promptInfo = createTestPromptInfo();
promptInfo.setAllowBackgroundAuthentication(false);
@@ -1193,7 +1213,7 @@ public class AuthControllerTest extends SysuiTestCase {
mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger,
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
- mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper,
+ mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager,
mLazyViewCapture, mMSDLPlayer);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 4e64c50a3253..297aee5c84c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics.domain.interactor
import android.graphics.Rect
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.view.MotionEvent
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -24,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.fingerprintManager
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -39,6 +42,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -57,6 +62,8 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
private val testScope: TestScope = kosmos.testScope
private val authController: AuthController = kosmos.authController
+ private val fingerprintManager: FingerprintManager = kosmos.fingerprintManager
+ @Mock private lateinit var fingerprintSensorProperties: FingerprintSensorPropertiesInternal
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
@@ -122,6 +129,20 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
context.orCreateTestableResources.removeOverride(R.dimen.pixel_pitch)
}
+ @Test
+ fun testSetIgnoreDisplayTouches() =
+ testScope.runTest {
+ createUdfpsOverlayInteractor()
+ whenever(authController.isUdfpsSupported).thenReturn(true)
+ whenever(authController.udfpsProps).thenReturn(listOf(fingerprintSensorProperties))
+
+ underTest.setHandleTouches(false)
+ verify(fingerprintManager).setIgnoreDisplayTouches(anyLong(), anyInt(), eq(true))
+
+ underTest.setHandleTouches(true)
+ verify(fingerprintManager).setIgnoreDisplayTouches(anyLong(), anyInt(), eq(false))
+ }
+
private fun createUdfpsOverlayInteractor() {
underTest = kosmos.udfpsOverlayInteractor
testScope.runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index d75c0138bcbf..ab936590de93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -1589,7 +1589,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo).isNotNull()
assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconWithBadge)
- assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionWithBadge)
+ // Logo label does not use badge info.
+ assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 84d062a3e7be..831012c88f7b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -30,7 +30,6 @@ import android.view.windowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.model.KeyPath
-import com.android.settingslib.Utils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
@@ -77,20 +76,14 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
private val contextDisplayInfo = DisplayInfo()
- private val indicatorColor =
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.materialColorPrimaryFixed,
+ private val indicatorColor = context.getColor(
+ com.android.internal.R.color.materialColorPrimaryFixed,
)
- private val outerRimColor =
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.materialColorPrimaryFixedDim,
+ private val outerRimColor = context.getColor(
+ com.android.internal.R.color.materialColorPrimaryFixedDim,
)
- private val chevronFill =
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.materialColorOnPrimaryFixed,
+ private val chevronFill = context.getColor(
+ com.android.internal.R.color.materialColorOnPrimaryFixed,
)
private val color_blue400 =
context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
index a308c8ee38ca..3f4d3f8ba12a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
@@ -98,6 +98,21 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun onMovedToDisplays_updatesOnMovedToDisplay() =
+ testScope.runTest {
+ val lastOnMovedToDisplay by collectLastValue(underTest.onMovedToDisplay)
+ assertThat(lastOnMovedToDisplay).isNull()
+
+ val configurationCallback = withArgCaptor {
+ verify(configurationController).addCallback(capture())
+ }
+
+ configurationCallback.onMovedToDisplay(1, Configuration())
+ runCurrent()
+ assertThat(lastOnMovedToDisplay).isEqualTo(1)
+ }
+
+ @Test
fun onAnyConfigurationChange_updatesOnConfigChanged() =
testScope.runTest {
val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index e5f0d7c6bf37..68f4acde7609 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -42,6 +42,7 @@ import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -494,6 +495,7 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
// Start dreaming.
updateDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
// Hub times out immediately.
assertThat(scene).isEqualTo(CommunalScenes.Blank)
@@ -650,6 +652,7 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
// Start dreaming.
updateDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
// Hub times out immediately.
assertThat(scene).isEqualTo(Scenes.Dream)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
new file mode 100644
index 000000000000..a8a3873d6de2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetHostView
+import android.platform.test.flag.junit.FlagsParameterization
+import android.util.SizeF
+import android.widget.RemoteViews
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SECONDARY_USER_WIDGET_HOST
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalAppWidgetViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+ val kosmos = testKosmos()
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ private val Kosmos.listenerDelegateFactory by
+ Kosmos.Fixture {
+ AppWidgetHostListenerDelegate.Factory { listener ->
+ AppWidgetHostListenerDelegate(fakeExecutor, listener)
+ }
+ }
+
+ private val Kosmos.appWidgetHost by
+ Kosmos.Fixture {
+ mock<CommunalAppWidgetHost> {
+ on { setListener(any(), any()) } doAnswer
+ { invocation ->
+ val callback = invocation.arguments[1] as AppWidgetHostListener
+ callback.updateAppWidget(mock<RemoteViews>())
+ }
+ }
+ }
+
+ private val Kosmos.glanceableHubWidgetManager by
+ Kosmos.Fixture {
+ mock<GlanceableHubWidgetManager> {
+ on { setAppWidgetHostListener(any(), any()) } doAnswer
+ { invocation ->
+ val callback = invocation.arguments[1] as AppWidgetHostListener
+ callback.updateAppWidget(mock<RemoteViews>())
+ }
+ }
+ }
+
+ private val Kosmos.underTest by
+ Kosmos.Fixture {
+ CommunalAppWidgetViewModel(
+ backgroundCoroutineContext,
+ { appWidgetHost },
+ listenerDelegateFactory,
+ { glanceableHubWidgetManager },
+ fakeGlanceableHubMultiUserHelper,
+ )
+ .apply { activateIn(testScope) }
+ }
+
+ @Test
+ fun setListener() =
+ kosmos.runTest {
+ val listener = mock<AppWidgetHostListener>()
+
+ underTest.setListener(123, listener)
+ runAll()
+
+ verify(listener).updateAppWidget(any())
+ }
+
+ @Test
+ fun setListener_HSUM() =
+ kosmos.runTest {
+ fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+ val listener = mock<AppWidgetHostListener>()
+
+ underTest.setListener(123, listener)
+ runAll()
+
+ verify(listener).updateAppWidget(any())
+ }
+
+ @Test
+ fun updateSize() =
+ kosmos.runTest {
+ val view = mock<AppWidgetHostView>()
+ val size = SizeF(/* width= */ 100f, /* height= */ 200f)
+
+ underTest.updateSize(size, view)
+ runAll()
+
+ verify(view)
+ .updateAppWidgetSize(
+ /* newOptions = */ any(),
+ /* minWidth = */ eq(100),
+ /* minHeight = */ eq(200),
+ /* maxWidth = */ eq(100),
+ /* maxHeight = */ eq(200),
+ /* ignorePadding = */ eq(true),
+ )
+ }
+
+ private fun Kosmos.runAll() {
+ runCurrent()
+ fakeExecutor.runAllReady()
+ }
+
+ private companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_SECONDARY_USER_WIDGET_HOST)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
new file mode 100644
index 000000000000..88206850eb60
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.service.dream.dreamManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.statusbar.policy.batteryController
+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.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+@RunWith(AndroidJUnit4::class)
+class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest: CommunalToDreamButtonViewModel by lazy {
+ kosmos.communalToDreamButtonViewModel
+ }
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ underTest.activateIn(testScope)
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_trueWhenCanDream() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(true)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isTrue()
+ }
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_falseWhenCannotDream() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(false)
+ whenever(batteryController.isPluggedIn()).thenReturn(true)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isFalse()
+ }
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(false)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isFalse()
+ }
+ }
+
+ @Test
+ fun onShowDreamButtonTap_startsDream() =
+ with(kosmos) {
+ runTest {
+ underTest.onShowDreamButtonTap()
+ runCurrent()
+
+ verify(dreamManager).startDream()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt
new file mode 100644
index 000000000000..b0f48038877e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt
@@ -0,0 +1,222 @@
+/*
+ * 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.communal.util
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ResizeUtilsTest : SysuiTestCase() {
+ private val mockWidget =
+ mock<CommunalContentModel.WidgetContent.Widget> {
+ on { size } doReturn CommunalContentSize.Responsive(1)
+ }
+
+ @Test
+ fun noOngoingContent() {
+ val list = listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized).containsExactly(mockWidget)
+ }
+
+ @Test
+ fun singleOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(CommunalContentSize.Responsive(1), mockWidget.size)
+ .inOrder()
+ }
+
+ @Test
+ fun singleOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(CommunalContentSize.Responsive(2), mockWidget.size)
+ .inOrder()
+ }
+
+ @Test
+ fun singleOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ // A spacer should be added as the second element to avoid mixing widget content
+ // with ongoing content.
+ assertThat(resized[1]).isInstanceOf(CommunalContentModel.Spacer::class.java)
+ }
+
+ @Test
+ fun twoOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun twoOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun twoOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ private fun createOngoingListWithSize(size: Int): List<CommunalContentModel.Ongoing> {
+ return List(size) { CommunalContentModel.Umo(createdTimestampMillis = 100) }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 763ea392deb9..d70af2806430 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.communal.view.viewmodel
import android.content.ComponentName
import android.content.pm.UserInfo
import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.widget.RemoteViews
@@ -26,6 +27,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
@@ -101,6 +103,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -169,7 +172,6 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
kosmos.testDispatcher,
testScope,
kosmos.testScope.backgroundScope,
- context.resources,
kosmos.keyguardTransitionInteractor,
kosmos.keyguardInteractor,
mock<KeyguardIndicationController>(),
@@ -442,6 +444,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
fun customizeWidgetButton_showsThenHidesAfterTimeout() =
testScope.runTest {
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
@@ -455,6 +458,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
fun customizeWidgetButton_onDismiss_hidesImmediately() =
testScope.runTest {
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
@@ -468,6 +472,14 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
+ fun longClickDirectlyStartsEditMode() =
+ testScope.runTest {
+ underTest.onLongClick()
+ verify(communalInteractor).showWidgetEditor(any())
+ }
+
+ @Test
fun canChangeScene_shadeNotExpanded() =
testScope.runTest {
// On keyguard without any shade expansion.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
index dd3f991e60b7..2ae611d236e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
@@ -133,6 +133,7 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
*/
@Test
public void testViewModelObservation() {
+ mController.onViewAttached();
final Observer<Collection<ComplicationViewModel>> observer =
captureComplicationViewModelsObserver();
@@ -152,6 +153,7 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
@Test
public void testMalformedComplicationAddition() {
+ mController.onViewAttached();
final Observer<Collection<ComplicationViewModel>> observer =
captureComplicationViewModelsObserver();
@@ -167,6 +169,7 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
@Test
public void testNewComplicationsBeforeEntryAnimationsFinishSetToInvisible() {
+ mController.onViewAttached();
final Observer<Collection<ComplicationViewModel>> observer =
captureComplicationViewModelsObserver();
@@ -181,6 +184,7 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
@Test
public void testNewComplicationsAfterEntryAnimationsFinishNotSetToInvisible() {
+ mController.onViewAttached();
final Observer<Collection<ComplicationViewModel>> observer =
captureComplicationViewModelsObserver();
@@ -198,6 +202,7 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
@Test
public void testAnimationsDisabled_ComplicationsNeverSetToInvisible() {
+ mController.onViewAttached();
//Disable animations
mController.mIsAnimationEnabled = false;
@@ -213,6 +218,16 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
}
+ @Test
+ public void testLifecycleObserve_activeOnlyDuringAttachedState() {
+ verify(mComplicationViewModelLiveData, never()).observe(any(), any());
+ mController.onViewAttached();
+ final Observer<Collection<ComplicationViewModel>> observer =
+ captureComplicationViewModelsObserver();
+ mController.onViewDetached();
+ verify(mComplicationViewModelLiveData).removeObserver(eq(observer));
+ }
+
private Observer<Collection<ComplicationViewModel>> captureComplicationViewModelsObserver() {
verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
mObserverCaptor.capture());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bbd78b317560..4f2c1ed6c778 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -50,6 +50,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -87,6 +88,8 @@ public class DozeScreenStateTest extends SysuiTestCase {
@Mock
private DozeScreenBrightness mDozeScreenBrightness;
@Mock
+ private DozeInteractor mDozeInteractor;
+ @Mock
private SelectedUserInteractor mSelectedUserInteractor;
@Before
@@ -103,7 +106,7 @@ public class DozeScreenStateTest extends SysuiTestCase {
mWakeLock = new WakeLockFake();
mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog,
- mDozeScreenBrightness, mSelectedUserInteractor);
+ mDozeScreenBrightness, mDozeInteractor, mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
index 790df03e6401..1e937b46dbcb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
@@ -29,8 +29,11 @@ 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.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.shared.condition.Monitor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.withArgCaptor
import kotlin.test.Test
import org.junit.Before
@@ -48,6 +51,8 @@ import org.mockito.kotlin.whenever
@TestableLooper.RunWithLooper
@RunWith(AndroidJUnit4::class)
class DreamOverlayRegistrantTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private val context = mock<Context>()
private val packageManager = mock<PackageManager>()
@@ -73,6 +78,7 @@ class DreamOverlayRegistrantTest : SysuiTestCase() {
monitor,
packageManager,
dreamManager,
+ kosmos.communalSettingsInteractor,
logBuffer,
)
@@ -117,7 +123,7 @@ class DreamOverlayRegistrantTest : SysuiTestCase() {
/** Verify overlay registered when enabled in manifest. */
@Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testRegisteredWhenEnabledWithManifest() {
serviceInfo.enabled = true
start()
@@ -127,8 +133,10 @@ class DreamOverlayRegistrantTest : SysuiTestCase() {
/** Verify overlay registered for mobile hub with flag. */
@Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testRegisteredForMobileHub() {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
start()
verify(dreamManager).registerDreamOverlayService(componentName)
@@ -139,7 +147,7 @@ class DreamOverlayRegistrantTest : SysuiTestCase() {
* enabled.
*/
@Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testDisabledForMobileWithoutMobileHub() {
start()
@@ -154,8 +162,9 @@ class DreamOverlayRegistrantTest : SysuiTestCase() {
/** Ensure service unregistered when component is disabled at runtime. */
@Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testUnregisteredWhenComponentDisabled() {
+ kosmos.setCommunalV2ConfigEnabled(true)
start()
verify(dreamManager).registerDreamOverlayService(componentName)
clearInvocations(dreamManager)
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 f924ccb42cb0..b07097d61b96 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -43,7 +43,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -55,7 +55,9 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.complication.ComplicationHostViewController
@@ -262,6 +264,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
mKeyguardUpdateMonitor,
mScrimManager,
mCommunalInteractor,
+ kosmos.communalSettingsInteractor,
kosmos.sceneInteractor,
mSystemDialogsCloser,
mUiEventLogger,
@@ -1283,7 +1286,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
environmentComponents.verifyNoMoreInteractions()
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun testAmbientTouchHandlersRegistration_registerHideComplicationAndCommunal() {
val client = client
@@ -1303,9 +1306,11 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
.containsExactly(mHideComplicationTouchHandler, mCommunalTouchHandler)
}
- @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun testAmbientTouchHandlersRegistration_v2_registerOnlyHideComplication() {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
val client = client
// Inform the overlay service of dream starting.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
index 76434ee54627..1de38ee3a04e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
@@ -16,10 +16,8 @@
package com.android.systemui.inputdevice.tutorial.ui.viewmodel
-import androidx.lifecycle.Lifecycle.Event
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -55,7 +53,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
import org.mockito.kotlin.mock
@OptIn(ExperimentalCoroutinesApi::class)
@@ -71,9 +68,6 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
private var tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD
private val viewModel by lazy { createViewModel(tutorialScope) }
- // createUnsafe so its methods don't have to be called on Main thread
- private val lifecycle = LifecycleRegistry.createUnsafe(mock(LifecycleOwner::class.java))
-
@get:Rule val mainDispatcherRule = MainDispatcherRule(kosmos.testDispatcher)
private fun createViewModel(
@@ -88,7 +82,6 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
mock<InputDeviceTutorialLogger>(),
SavedStateHandle(mapOf(INTENT_TUTORIAL_SCOPE_KEY to scope)),
)
- lifecycle.addObserver(viewModel)
return viewModel
}
@@ -279,7 +272,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
collectValues(viewModel.screen) // just to initialize viewModel
peripheralsState(touchpadConnected = true)
- lifecycle.handleLifecycleEvent(Event.ON_START)
+ viewModel.onStart(TestLifecycleOwner())
assertGesturesDisabled()
}
@@ -291,8 +284,8 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
collectValues(viewModel.screen)
peripheralsState(touchpadConnected = true)
- lifecycle.handleLifecycleEvent(Event.ON_START)
- lifecycle.handleLifecycleEvent(Event.ON_STOP)
+ viewModel.onStart(TestLifecycleOwner())
+ viewModel.onStop(TestLifecycleOwner())
assertGesturesNotDisabled()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/OWNERS
new file mode 100644
index 000000000000..2355c48158f7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1562219
+chrisgollner@google.com
+jmokut@google.com \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt
new file mode 100644
index 000000000000..477e31e8a66c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.keyboard.shortcut.data.repository
+
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.fakeInputManager
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CTRL_ON
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.shortcut.appLaunchDataRepository
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCommand
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AppLaunchDataRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val inputManager = kosmos.fakeInputManager.inputManager
+ private val testHelper = kosmos.shortcutHelperTestHelper
+ private val repo = kosmos.appLaunchDataRepository
+
+ @Test
+ fun appLaunchData_returnsDataRetrievedFromApiBasedOnShortcutCommand() =
+ kosmos.runTest {
+ val inputGesture = simpleInputGestureDataForAppLaunchShortcut()
+ setApiAppLaunchBookmarks(listOf(inputGesture))
+
+ testHelper.toggle(TEST_DEVICE_ID)
+
+ val appLaunchData =
+ repo.getAppLaunchDataForShortcutWithCommand(
+ shortcutCommand =
+ shortcutCommand {
+ key("Ctrl")
+ key("Alt")
+ key("A")
+ }
+ )
+
+ assertThat(appLaunchData).isEqualTo(inputGesture.action.appLaunchData())
+ }
+
+ @Test
+ fun appLaunchData_returnsSameDataForAnyOrderOfShortcutCommandKeys() =
+ kosmos.runTest {
+ val inputGesture = simpleInputGestureDataForAppLaunchShortcut()
+ setApiAppLaunchBookmarks(listOf(inputGesture))
+
+ testHelper.toggle(TEST_DEVICE_ID)
+
+ val shortcutCommandCtrlAltA = shortcutCommand {
+ key("Ctrl")
+ key("Alt")
+ key("A")
+ }
+
+ val shortcutCommandCtrlAAlt = shortcutCommand {
+ key("Ctrl")
+ key("A")
+ key("Alt")
+ }
+
+ val shortcutCommandAltCtrlA = shortcutCommand {
+ key("Alt")
+ key("Ctrl")
+ key("A")
+ }
+
+ assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandCtrlAltA))
+ .isEqualTo(inputGesture.action.appLaunchData())
+
+ assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandCtrlAAlt))
+ .isEqualTo(inputGesture.action.appLaunchData())
+
+ assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandAltCtrlA))
+ .isEqualTo(inputGesture.action.appLaunchData())
+ }
+
+ private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
+ whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
+ }
+
+ private fun simpleInputGestureDataForAppLaunchShortcut(
+ keyCode: Int = KEYCODE_A,
+ modifiers: Int = META_CTRL_ON or META_ALT_ON,
+ appLaunchData: AppLaunchData = RoleData(TEST_ROLE),
+ ): InputGestureData {
+ return InputGestureData.Builder()
+ .setTrigger(createKeyTrigger(keyCode, modifiers))
+ .setAppLaunchData(appLaunchData)
+ .build()
+ }
+
+ private companion object {
+ private const val TEST_ROLE = "Test role"
+ private const val TEST_DEVICE_ID = 123
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index d12c04586ac2..4cfb26e6555b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -18,14 +18,21 @@ package com.android.systemui.keyboard.shortcut.data.repository
import android.content.Context
import android.content.Context.INPUT_SERVICE
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
import android.hardware.input.fakeInputManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.view.KeyEvent.KEYCODE_A
import android.view.KeyEvent.KEYCODE_SLASH
+import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CAPS_LOCK_ON
+import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -44,14 +51,15 @@ import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCusto
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.launchCalendarShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Add
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Delete
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
@@ -72,7 +80,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
private val mockUserContext: Context = mock()
private val kosmos =
- testKosmos().also {
+ testKosmos().useUnconfinedTestDispatcher().also {
it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
@@ -242,6 +250,32 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
}
@Test
+ fun buildInputGestureDataForAppLaunchShortcut_keyGestureTypeIsTypeLaunchApp() =
+ testScope.runTest {
+ setApiAppLaunchBookmarks(listOf(simpleInputGestureDataForAppLaunchShortcut()))
+ helper.toggle(deviceId = 123)
+ repo.onCustomizationRequested(launchCalendarShortcutAddRequest)
+ repo.updateUserKeyCombination(standardKeyCombination)
+
+ val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
+
+ assertThat(inputGestureData?.action?.keyGestureType())
+ .isEqualTo(KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ }
+
+ @Test
+ fun buildInputGestureDataForAppLaunchShortcut_appLaunchDataIsAdded() =
+ testScope.runTest {
+ setApiAppLaunchBookmarks(listOf(simpleInputGestureDataForAppLaunchShortcut()))
+ helper.toggle(deviceId = 123)
+ repo.onCustomizationRequested(launchCalendarShortcutAddRequest)
+ repo.updateUserKeyCombination(standardKeyCombination)
+
+ val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
+ assertThat(inputGestureData?.action?.appLaunchData()).isNotNull()
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
fun deleteShortcut_successfullyRetrievesGestureDataAndDeletesShortcut() {
testScope.runTest {
@@ -304,17 +338,17 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
private suspend fun customizeShortcut(
customizationRequest: ShortcutCustomizationRequestInfo,
- keyCombination: KeyCombination? = null
- ): ShortcutCustomizationRequestResult{
+ keyCombination: KeyCombination? = null,
+ ): ShortcutCustomizationRequestResult {
repo.onCustomizationRequested(customizationRequest)
repo.updateUserKeyCombination(keyCombination)
return when (customizationRequest) {
- is Add -> {
+ is SingleShortcutCustomization.Add -> {
repo.confirmAndSetShortcutCurrentlyBeingCustomized()
}
- is Delete -> {
+ is SingleShortcutCustomization.Delete -> {
repo.deleteShortcutCurrentlyBeingCustomized()
}
@@ -352,4 +386,19 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
assertThat(categories).isEmpty()
}
}
+
+ private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
+ whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
+ }
+
+ private fun simpleInputGestureDataForAppLaunchShortcut(
+ keyCode: Int = KEYCODE_A,
+ modifiers: Int = META_CTRL_ON or META_ALT_ON,
+ appLaunchData: AppLaunchData = RoleData("Test role"),
+ ): InputGestureData {
+ return InputGestureData.Builder()
+ .setTrigger(createKeyTrigger(keyCode, modifiers))
+ .setAppLaunchData(appLaunchData)
+ .build()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
index f90ab1fcc75b..3bf59f34db76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
@@ -40,6 +40,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.recentAppsGroup
import com.android.systemui.keyboard.shortcut.defaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
@@ -270,6 +273,31 @@ class DefaultShortcutCategoriesRepositoryTest : SysuiTestCase() {
assertThat(systemCategory).isEqualTo(expectedCategory)
}
+ @Test
+ fun categories_recentAppsSwitchShortcutsIsMarkedNonCustomizable() {
+ testScope.runTest {
+ helper.setImeShortcuts(emptyList())
+ fakeSystemSource.setGroups(emptyList())
+ fakeMultiTaskingSource.setGroups(recentAppsGroup)
+
+ helper.showFromActivity()
+ val categories by collectLastValue(repo.categories)
+
+ val cycleForwardThroughRecentAppsShortcut =
+ categories?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+
+ val cycleBackThroughRecentAppsShortcut =
+ categories?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+
+ assertThat(cycleForwardThroughRecentAppsShortcut?.isCustomizable).isFalse()
+ assertThat(cycleBackThroughRecentAppsShortcut?.isCustomizable).isFalse()
+ }
+ }
+
private fun simpleSubCategory(vararg shortcuts: Shortcut) =
ShortcutSubCategory(simpleGroupLabel, shortcuts.asList())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
new file mode 100644
index 000000000000..96410597e20c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.keyboard.shortcut.data.repository
+
+import android.app.role.RoleManager
+import android.app.role.roleManager
+import android.content.Context
+import android.content.Intent
+import android.content.mockedContext
+import android.content.packageManager
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CTRL_ON
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.app.ResolverActivity
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.shortcut.data.model.InternalGroupsSource
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.inputGestureDataAdapter
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.res.R
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
+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.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InputGestureDataAdapterTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().also { kosmos ->
+ kosmos.userTracker =
+ FakeUserTracker(onCreateCurrentUserContext = { kosmos.mockedContext })
+ }
+ private val adapter = kosmos.inputGestureDataAdapter
+ private val roleManager = kosmos.roleManager
+ private val packageManager: PackageManager = kosmos.packageManager
+ private val mockUserContext: Context = kosmos.mockedContext
+ private val intent: Intent = mock()
+ private val fakeResolverActivityInfo =
+ ActivityInfo().apply { name = ResolverActivity::class.qualifiedName }
+ private val fakeActivityInfo: ActivityInfo =
+ ActivityInfo().apply {
+ name = FAKE_ACTIVITY_NAME
+ icon = 0x1
+ nonLocalizedLabel = TEST_SHORTCUT_LABEL
+ }
+ private val mockSelectorIntent: Intent = mock()
+
+ @Before
+ fun setup() {
+ whenever(mockUserContext.packageManager).thenReturn(packageManager)
+ whenever(mockUserContext.getSystemService(RoleManager::class.java)).thenReturn(roleManager)
+ whenever(roleManager.isRoleAvailable(TEST_ROLE)).thenReturn(true)
+ whenever(roleManager.getDefaultApplication(TEST_ROLE)).thenReturn(TEST_ROLE_PACKAGE)
+ whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(mock())
+ whenever(packageManager.getLaunchIntentForPackage(TEST_ROLE_PACKAGE)).thenReturn(intent)
+ whenever(intent.selector).thenReturn(mockSelectorIntent)
+ whenever(mockSelectorIntent.categories).thenReturn(setOf(TEST_ACTIVITY_CATEGORY))
+ }
+
+ @Test
+ fun shortcutLabel_whenDefaultAppForCategoryIsNotSet_loadsLabelFromFirstAppMatchingIntent() =
+ kosmos.runTest {
+ setApiToRetrieveResolverActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val label =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.label
+
+ assertThat(label).isEqualTo(expectedShortcutLabelForFirstAppMatchingIntent)
+ }
+
+ @Test
+ fun shortcutLabel_whenDefaultAppForCategoryIsSet_loadsLabelOfDefaultApp() {
+ kosmos.runTest {
+ setApiToRetrieveSpecificActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val label =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.label
+
+ assertThat(label).isEqualTo(TEST_SHORTCUT_LABEL)
+ }
+ }
+
+ @Test
+ fun shortcutIcon_whenDefaultAppForCategoryIsSet_loadsIconOfDefaultApp() {
+ kosmos.runTest {
+ setApiToRetrieveSpecificActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val icon =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.icon
+
+ assertThat(icon).isNotNull()
+ }
+ }
+
+ @Test
+ fun internalGroupSource_isCorrectlyConvertedWithSimpleInputGestureData() =
+ kosmos.runTest {
+ setApiToRetrieveResolverActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+
+ assertThat(internalGroups)
+ .containsExactly(
+ InternalGroupsSource(
+ type = ShortcutCategoryType.AppCategories,
+ groups =
+ listOf(
+ InternalKeyboardShortcutGroup(
+ label = APPLICATION_SHORTCUT_GROUP_LABEL,
+ items =
+ listOf(
+ InternalKeyboardShortcutInfo(
+ label =
+ expectedShortcutLabelForFirstAppMatchingIntent,
+ keycode = KEYCODE_A,
+ modifiers = META_CTRL_ON or META_ALT_ON,
+ isCustomShortcut = true,
+ )
+ ),
+ )
+ ),
+ )
+ )
+ }
+
+ @Test
+ fun keyGestureType_returnsTypeLaunchApplicationForAppLaunchShortcutCategory() =
+ kosmos.runTest {
+ assertThat(
+ adapter.getKeyGestureTypeForShortcut(
+ shortcutLabel = "Test Shortcut label",
+ shortcutCategoryType = ShortcutCategoryType.AppCategories,
+ )
+ )
+ .isEqualTo(KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ }
+
+ private fun setApiToRetrieveResolverActivity() {
+ whenever(intent.resolveActivityInfo(eq(packageManager), anyInt()))
+ .thenReturn(fakeResolverActivityInfo)
+ }
+
+ private fun setApiToRetrieveSpecificActivity() {
+ whenever(intent.resolveActivityInfo(eq(packageManager), anyInt()))
+ .thenReturn(fakeActivityInfo)
+ }
+
+ private fun buildInputGestureDataForAppLaunchShortcut(
+ keyCode: Int = KEYCODE_A,
+ modifiers: Int = META_CTRL_ON or META_ALT_ON,
+ appLaunchData: AppLaunchData = RoleData(TEST_ROLE),
+ ): InputGestureData {
+ return InputGestureData.Builder()
+ .setTrigger(createKeyTrigger(keyCode, modifiers))
+ .setAppLaunchData(appLaunchData)
+ .build()
+ }
+
+ private val expectedShortcutLabelForFirstAppMatchingIntent =
+ context.getString(R.string.keyboard_shortcut_group_applications_browser)
+
+ private companion object {
+ private const val TEST_ROLE = "Test Browser Role"
+ private const val TEST_ROLE_PACKAGE = "test.browser.package"
+ private const val APPLICATION_SHORTCUT_GROUP_LABEL = "Applications"
+ private const val FAKE_ACTIVITY_NAME = "Fake activity"
+ private const val TEST_SHORTCUT_LABEL = "Test shortcut label"
+ private const val TEST_ACTIVITY_CATEGORY = Intent.CATEGORY_APP_BROWSER
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt
new file mode 100644
index 000000000000..ded2d223aab4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.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.keyboard.shortcut.shortcutHelperInputDeviceRepository
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperInputDeviceRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testHelper = kosmos.shortcutHelperTestHelper
+ private val repo = kosmos.shortcutHelperInputDeviceRepository
+
+ @Test
+ fun activeInputDevice_nullByDefault() =
+ kosmos.runTest {
+ val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+ assertThat(activeInputDevice).isNull()
+ }
+
+ @Test
+ fun activeInputDevice_nonNullWhenHelperIsShown() =
+ kosmos.runTest {
+ val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+ testHelper.showFromActivity()
+
+ assertThat(activeInputDevice).isNotNull()
+ }
+
+ @Test
+ fun activeInputDevice_nullWhenHelperIsClosed() =
+ kosmos.runTest {
+ val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+ testHelper.showFromActivity()
+ testHelper.hideFromActivity()
+
+ assertThat(activeInputDevice).isNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index cc718c784c78..7c88d76f28bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -40,15 +40,15 @@ import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCommand
import com.android.systemui.res.R
object TestShortcuts {
@@ -57,14 +57,14 @@ object TestShortcuts {
KeyboardShortcutInfo(
/* label = */ "Shortcut with repeated label",
/* keycode = */ KeyEvent.KEYCODE_H,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
)
private val shortcutInfoWithRepeatedLabelAlternate =
KeyboardShortcutInfo(
/* label = */ shortcutInfoWithRepeatedLabel.label,
/* keycode = */ KeyEvent.KEYCODE_L,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
)
private val shortcutInfoWithRepeatedLabelSecondAlternate =
@@ -126,9 +126,44 @@ object TestShortcuts {
KeyboardShortcutInfo(
/* label = */ "Standard shortcut 1",
/* keycode = */ KeyEvent.KEYCODE_N,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
+ )
+
+ const val CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL = "Cycle forward through recent apps"
+ const val CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL = "Cycle backward through recent apps"
+
+ private val recentAppsCycleForwardShortcutInfo =
+ KeyboardShortcutInfo(
+ /* label = */ CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL,
+ /* keycode = */ KeyEvent.KEYCODE_N,
+ /* modifiers = */ META_META_ON,
+ )
+
+ private val recentAppsCycleBackShortcutInfo =
+ KeyboardShortcutInfo(
+ /* label = */ CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL,
+ /* keycode = */ KeyEvent.KEYCODE_N,
+ /* modifiers = */ META_META_ON,
)
+ private val recentAppsCycleForwardShortcut =
+ shortcut(recentAppsCycleForwardShortcutInfo.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("N")
+ }
+ isCustomizable = false
+ }
+
+ private val recentAppsCycleBackShortcut =
+ shortcut(recentAppsCycleBackShortcutInfo.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("N")
+ }
+ isCustomizable = false
+ }
+
private val standardShortcut1 =
shortcut(standardShortcutInfo1.label!!.toString()) {
command {
@@ -186,7 +221,7 @@ object TestShortcuts {
KeyboardShortcutInfo(
/* label = */ "Shortcut with unsupported modifiers",
/* keycode = */ KeyEvent.KEYCODE_A,
- /* modifiers = */ KeyEvent.META_META_ON or KeyEvent.KEYCODE_SPACE,
+ /* modifiers = */ META_META_ON or KeyEvent.KEYCODE_SPACE,
)
private val groupWithRepeatedShortcutLabels =
@@ -262,6 +297,12 @@ object TestShortcuts {
listOf(standardShortcut3),
)
+ val recentAppsGroup =
+ KeyboardShortcutGroup(
+ "Recent apps",
+ listOf(recentAppsCycleForwardShortcutInfo, recentAppsCycleBackShortcutInfo),
+ )
+
private val standardGroup1 =
KeyboardShortcutGroup(
"Standard group 1",
@@ -280,6 +321,12 @@ object TestShortcuts {
private val standardSystemAppSubcategoryWithCustomHomeShortcut =
ShortcutSubCategory("System controls", listOf(customGoHomeShortcut))
+ private val recentAppsSubCategory =
+ ShortcutSubCategory(
+ recentAppsGroup.label!!.toString(),
+ listOf(recentAppsCycleForwardShortcut, recentAppsCycleBackShortcut),
+ )
+
private val standardSubCategory1 =
ShortcutSubCategory(
standardGroup1.label!!.toString(),
@@ -375,6 +422,9 @@ object TestShortcuts {
),
)
+ val multitaskingCategoryWithRecentAppsGroup =
+ ShortcutCategory(type = MultiTasking, subCategories = listOf(recentAppsSubCategory))
+
val multitaskingGroups = listOf(standardGroup2, standardGroup1)
val multitaskingCategory =
ShortcutCategory(
@@ -439,6 +489,7 @@ object TestShortcuts {
category: ShortcutCategoryType,
subcategoryLabel: String,
shortcutLabel: String,
+ includeInCustomization: Boolean = true,
): ShortcutCategory {
return ShortcutCategory(
type = category,
@@ -446,13 +497,13 @@ object TestShortcuts {
listOf(
ShortcutSubCategory(
label = subcategoryLabel,
- shortcuts = listOf(simpleShortcut(shortcutLabel)),
+ shortcuts = listOf(simpleShortcut(shortcutLabel, includeInCustomization)),
)
),
)
}
- private fun simpleShortcut(label: String) =
+ private fun simpleShortcut(label: String, includeInCustomization: Boolean = true) =
Shortcut(
label = label,
commands =
@@ -467,6 +518,7 @@ object TestShortcuts {
),
)
),
+ isCustomizable = includeInCustomization,
)
val customizableInputGestureWithUnknownKeyGestureType =
@@ -491,37 +543,20 @@ object TestShortcuts {
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Switch from split screen to full screen",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Use split screen with current app on the left",
+ "Switch to full screen",
),
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Switch to app on left or above while using split screen",
+ "Use split screen with app on the left",
),
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Use split screen with current app on the right",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Switch to app on right or below while using split screen",
+ "Use split screen with app on the right",
),
simpleShortcutCategory(System, "System controls", "Show shortcuts"),
simpleShortcutCategory(System, "System controls", "View recent apps"),
- simpleShortcutCategory(AppCategories, "Applications", "Calculator"),
- simpleShortcutCategory(AppCategories, "Applications", "Calendar"),
- simpleShortcutCategory(AppCategories, "Applications", "Browser"),
- simpleShortcutCategory(AppCategories, "Applications", "Contacts"),
- simpleShortcutCategory(AppCategories, "Applications", "Email"),
- simpleShortcutCategory(AppCategories, "Applications", "Maps"),
- simpleShortcutCategory(AppCategories, "Applications", "SMS"),
)
val customInputGestureTypeHome = simpleInputGestureData(keyGestureType = KEY_GESTURE_TYPE_HOME)
@@ -550,53 +585,39 @@ object TestShortcuts {
keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
),
simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
- ),
- simpleInputGestureData(
keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
),
simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
- ),
- simpleInputGestureData(
keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
),
simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
- ),
- simpleInputGestureData(
keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
),
)
val allAppsShortcutAddRequest =
- ShortcutCustomizationRequestInfo.Add(
+ SingleShortcutCustomization.Add(
label = "Open apps list",
categoryType = System,
subCategoryLabel = "System controls",
)
+ val launchCalendarShortcutAddRequest =
+ SingleShortcutCustomization.Add(
+ label = "Calendar",
+ categoryType = ShortcutCategoryType.AppCategories,
+ subCategoryLabel = "Applications",
+ shortcutCommand =
+ shortcutCommand {
+ key("Ctrl")
+ key("Alt")
+ key("A")
+ },
+ )
+
val allAppsShortcutDeleteRequest =
- ShortcutCustomizationRequestInfo.Delete(
+ SingleShortcutCustomization.Delete(
label = "Open apps list",
categoryType = System,
subCategoryLabel = "System controls",
@@ -691,7 +712,7 @@ object TestShortcuts {
)
val standardAddShortcutRequest =
- ShortcutCustomizationRequestInfo.Add(
+ SingleShortcutCustomization.Add(
label = "Standard shortcut",
categoryType = System,
subCategoryLabel = "Standard subcategory",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
index 77c615cce287..789b10b7830c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
@@ -25,7 +25,8 @@ import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.setCommunalEnabled
+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.coroutines.collectLastValue
import com.android.systemui.flags.andSceneContainer
@@ -38,7 +39,6 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,7 +48,7 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON)
+@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON, Flags.FLAG_GLANCEABLE_HUB_V2)
@RunWith(ParameterizedAndroidJunit4::class)
class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -69,6 +69,7 @@ class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : Sy
context = context,
communalInteractor = kosmos.communalInteractor,
communalSceneRepository = kosmos.communalSceneRepository,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = kosmos.sceneInteractor,
)
}
@@ -76,28 +77,30 @@ class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : Sy
@Test
fun lockscreenState_whenGlanceableHubEnabled_returnsVisible() =
testScope.runTest {
- kosmos.setCommunalEnabled(true)
+ kosmos.setCommunalV2Enabled(true)
runCurrent()
val lockScreenState by collectLastValue(underTest.lockScreenState)
- assertTrue(lockScreenState is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertThat(lockScreenState)
+ .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
}
@Test
fun lockscreenState_whenGlanceableHubDisabled_returnsHidden() =
testScope.runTest {
- kosmos.setCommunalEnabled(false)
+ kosmos.setCommunalV2Enabled(false)
val lockScreenState by collectLastValue(underTest.lockScreenState)
runCurrent()
- assertTrue(lockScreenState is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ assertThat(lockScreenState)
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
}
@Test
fun pickerScreenState_whenGlanceableHubEnabled_returnsDefault() =
testScope.runTest {
- kosmos.setCommunalEnabled(true)
+ kosmos.setCommunalV2Enabled(true)
runCurrent()
assertThat(underTest.getPickerScreenState())
@@ -107,7 +110,7 @@ class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : Sy
@Test
fun pickerScreenState_whenGlanceableHubDisabled_returnsDisabled() =
testScope.runTest {
- kosmos.setCommunalEnabled(false)
+ kosmos.setCommunalV2Enabled(false)
runCurrent()
assertThat(
@@ -143,7 +146,8 @@ class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : Sy
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON
+ Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON,
+ Flags.FLAG_GLANCEABLE_HUB_V2,
)
.andSceneContainer()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index e07961959f1b..635f80262348 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -995,12 +995,12 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
- sendSteps(sendStep1)
- kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ sendSteps(sendStep1, sendStep2)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
- sendSteps(sendStep2, sendStep3, sendStep4)
+ sendSteps(sendStep3, sendStep4)
assertEquals(listOf<TransitionStep>(), currentStatesMapped)
}
@@ -1134,6 +1134,63 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
)
}
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_out_of_lockscreen_scene() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, Scenes.Gone)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
+ val sendStep1 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ val sendStep2 = TransitionStep(UNDEFINED, LOCKSCREEN, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep4 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_into_lockscreen_scene() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(Scenes.Gone, LOCKSCREEN)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ val sendStep4 = TransitionStep(UNDEFINED, LOCKSCREEN, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_out_of_ls_with_wildcard() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(to = LOCKSCREEN)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ val sendStep4 = TransitionStep(UNDEFINED, LOCKSCREEN, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
private suspend fun sendSteps(vararg steps: TransitionStep) {
steps.forEach {
repository.sendTransitionStep(it)
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 21019875f51e..9d5bf4dbdc3f 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
@@ -55,7 +55,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -887,7 +886,6 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
@Test
@EnableSceneContainer
- @Ignore("b/378766637")
fun lockscreenVisibilityWithScenes() =
testScope.runTest {
val isDeviceUnlocked by
@@ -896,6 +894,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
)
assertThat(isDeviceUnlocked).isFalse()
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index 9fab60374f8b..70d7a5f2bdc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -27,7 +27,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,6 +49,8 @@ class AlternateBouncerToPrimaryBouncerTransitionViewModelTest : SysuiTestCase()
private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
private val underTest by lazy { kosmos.alternateBouncerToPrimaryBouncerTransitionViewModel }
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
@Test
fun deviceEntryParentViewDisappear() =
testScope.runTest {
@@ -67,13 +71,44 @@ class AlternateBouncerToPrimaryBouncerTransitionViewModelTest : SysuiTestCase()
values.forEach { assertThat(it).isEqualTo(0f) }
}
+ @Test
+ fun blurRadiusGoesToMaximumWhenShadeIsExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+ startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ checkInterpolatedValues = false,
+ transitionFactory = ::step,
+ actualValuesProvider = { values },
+ )
+ }
+
+ @Test
+ fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ transitionFactory = ::step,
+ actualValuesProvider = { values },
+ )
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.ALTERNATE_BOUNCER,
to = KeyguardState.PRIMARY_BOUNCER,
value = value,
transitionState = state,
- ownerName = "AlternateBouncerToPrimaryBouncerTransitionViewModelTest"
+ ownerName = "AlternateBouncerToPrimaryBouncerTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index 5e9badc58d8b..4dbe7c8bdb5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -171,8 +172,10 @@ class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization) : Sys
// WHEN transition is cancelled
repository.sendTransitionStep(step(.1f, TransitionState.CANCELED))
- // THEN alpha is immediately set to 1f (expected lockscreen alpha state)
- assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+ // THEN alpha updates according to whether the scene framework is enabled (CANCELED is
+ // ignored when the scene framework is enabled).
+ assertThat(deviceEntryBackgroundViewAlpha)
+ .isEqualTo(if (SceneContainerFlag.isEnabled) 0f else 1f)
}
@Test
@@ -195,14 +198,14 @@ class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization) : Sys
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
value = value,
transitionState = state,
- ownerName = "AodToLockscreenTransitionViewModelTest"
+ ownerName = "AodToLockscreenTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
new file mode 100644
index 000000000000..0f239e8472a8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.ui.viewmodel
+
+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.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest by lazy { kosmos.aodToPrimaryBouncerTransitionViewModel }
+
+ @Test
+ fun aodToPrimaryBouncerChangesBlurToMax() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 1.0f),
+ startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ transitionFactory = { value, state ->
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
+ )
+ },
+ actualValuesProvider = { values },
+ checkInterpolatedValues = false,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt
new file mode 100644
index 000000000000..c3f0deb925e4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.ui.viewmodel
+
+import android.util.MathUtils
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.ShadeTestUtil
+import com.android.systemui.shade.shadeTestUtil
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+
+val Kosmos.bouncerWindowBlurTestUtil by
+ Kosmos.Fixture {
+ BouncerWindowBlurTestUtil(
+ shadeTestUtil = shadeTestUtil,
+ fakeKeyguardTransitionRepository = fakeKeyguardTransitionRepository,
+ fakeKeyguardRepository = fakeKeyguardRepository,
+ testScope = testScope,
+ )
+ }
+
+class BouncerWindowBlurTestUtil(
+ private val shadeTestUtil: ShadeTestUtil,
+ private val fakeKeyguardTransitionRepository: FakeKeyguardTransitionRepository,
+ private val fakeKeyguardRepository: FakeKeyguardRepository,
+ private val testScope: TestScope,
+) {
+
+ suspend fun assertTransitionToBlurRadius(
+ transitionProgress: List<Float>,
+ startValue: Float,
+ endValue: Float,
+ actualValuesProvider: () -> List<Float>,
+ transitionFactory: (value: Float, state: TransitionState) -> TransitionStep,
+ checkInterpolatedValues: Boolean = true,
+ ) {
+ val transitionSteps =
+ listOf(
+ transitionFactory(transitionProgress.first(), STARTED),
+ *transitionProgress.drop(1).map { transitionFactory(it, RUNNING) }.toTypedArray(),
+ )
+ fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+
+ val interpolationFunction = { step: Float -> MathUtils.lerp(startValue, endValue, step) }
+
+ if (checkInterpolatedValues) {
+ assertThat(actualValuesProvider.invoke())
+ .containsExactly(*transitionProgress.map(interpolationFunction).toTypedArray())
+ } else {
+ assertThat(actualValuesProvider.invoke()).contains(endValue)
+ }
+ }
+
+ fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeTestUtil.setQsExpansion(1f)
+ } else {
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeTestUtil.setQsExpansion(0f)
+ shadeTestUtil.setLockscreenShadeExpansion(0f)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
index bf71bec9e1f6..7a68d4eda654 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -70,13 +71,27 @@ class DozingToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
values.forEach { assertThat(it).isEqualTo(0f) }
}
+ @Test
+ fun windowBlurRadiusGoesFromMinToMax() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ )
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.DOZING,
to = KeyguardState.PRIMARY_BOUNCER,
value = value,
transitionState = state,
- ownerName = "DozingToPrimaryBouncerTransitionViewModelTest"
+ ownerName = "DozingToPrimaryBouncerTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index d0da2e9671c0..576795d7e293 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeTestUtil
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
@@ -178,11 +179,13 @@ class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization)
),
testScope = testScope,
)
- assertThat(values.size).isEqualTo(3)
+ assertThat(values.size).isEqualTo(if (SceneContainerFlag.isEnabled) 2 else 3)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
- // Cancel will reset the translation
- assertThat(values[2]).isEqualTo(0)
+ // When the scene framework is not enabled, cancel will reset the translation
+ if (!SceneContainerFlag.isEnabled) {
+ assertThat(values.last()).isEqualTo(0f)
+ }
}
@Test
@@ -242,8 +245,9 @@ class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization)
// WHEN transition is canceled
repository.sendTransitionStep(step(1f, TransitionState.CANCELED))
- // THEN alpha is immediately set to 0f
- assertThat(actual).isEqualTo(0f)
+ // THEN alpha updates according to whether the scene framework is enabled (CANCELED is
+ // ignored when the scene framework is enabled).
+ assertThat(actual).isEqualTo(if (SceneContainerFlag.isEnabled) 1f else 0f)
}
private fun step(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 1c1fcc450d73..fba39970ddb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
@@ -31,6 +32,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -128,7 +130,7 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
emptyFlow(),
emptyFlow(),
false,
- emptyFlow()
+ emptyFlow(),
)
runCurrent()
// fade out
@@ -150,6 +152,39 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
Truth.assertThat(actual).isEqualTo(0f)
}
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun blurRadiusIsMaxWhenShadeIsExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ )
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING,
@@ -161,7 +196,7 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
else KeyguardState.PRIMARY_BOUNCER,
value = value,
transitionState = state,
- ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
+ ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest",
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
index c55c27c3b516..b406e6cdef37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -21,11 +21,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -138,16 +140,31 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() {
assertThat(deviceEntryParentViewAlpha).isNull()
}
+ @Test
+ fun blurRadiusGoesToMinImmediately() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.AOD,
value = value,
transitionState = state,
- ownerName = "PrimaryBouncerToAodTransitionViewModelTest"
+ ownerName = "PrimaryBouncerToAodTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
index 28473b204ce3..a8f0f2f453df 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -122,13 +123,28 @@ class PrimaryBouncerToDozingTransitionViewModelTest : SysuiTestCase() {
values.forEach { assertThat(it).isEqualTo(0f) }
}
+ @Test
+ fun blurRadiusGoesToMinImmediately() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.DOZING,
value = value,
transitionState = state,
- ownerName = "PrimaryBouncerToDozingTransitionViewModelTest"
+ ownerName = "PrimaryBouncerToDozingTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 000000000000..2c6e553dae90
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.ui.viewmodel
+
+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.flags.DisableSceneContainer
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest by lazy { kosmos.primaryBouncerToGlanceableHubTransitionViewModel }
+
+ @Test
+ @DisableSceneContainer
+ fun blurBecomesMinValueImmediately() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = { step, transitionState ->
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB,
+ value = step,
+ transitionState = transitionState,
+ ownerName = "PrimaryBouncerToGlanceableHubTransitionViewModelTest",
+ )
+ },
+ checkInterpolatedValues = false,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 5ec566bab6d5..9e5976f914ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -21,11 +21,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.collect.Range
@@ -110,16 +112,47 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
assertThat(bgViewAlpha).isEqualTo(1f)
}
+ @Test
+ fun blurRadiusGoesFromMaxToMinWhenShadeIsNotExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ )
+ }
+
+ @Test
+ fun blurRadiusRemainsAtMaxWhenShadeIsExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+ kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.LOCKSCREEN,
value = value,
transitionState = state,
- ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest"
+ ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt
new file mode 100644
index 000000000000..49e553b56554
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kosmos
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GeneralKosmosTest : SysuiTestCase() {
+ @Test
+ fun stateCurrentValueMutableStateFlow() = runTest {
+ val source = MutableStateFlow(1)
+ val mapped =
+ source
+ .map { it * 2 }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = source.value * 2,
+ )
+ assertThat(currentValue(mapped)).isEqualTo(2)
+
+ source.value = 3
+ assertThat(currentValue(mapped)).isEqualTo(6)
+ }
+
+ @Test
+ fun stateCurrentValueOnEmittedFlow() = runTest {
+ val source = flow {
+ emit(1)
+ emit(2)
+ }
+ val mapped =
+ source
+ .map { it * 2 }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = 2,
+ )
+ assertThat(currentValue(mapped)).isEqualTo(4)
+ }
+
+ @Test
+ fun currentValueIsNull() = runTest {
+ val source = MutableStateFlow<Int?>(null)
+ assertThat(currentValue(source)).isEqualTo(null)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
index 01220285e10c..6a2aae175d80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
@@ -16,21 +16,23 @@
package com.android.systemui.media.controls.ui.viewmodel
-import android.R
import android.content.packageManager
import android.content.pm.ApplicationInfo
import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
+import androidx.constraintlayout.widget.ConstraintSet
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.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -99,14 +101,14 @@ class MediaControlViewModelTest : SysuiTestCase() {
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isFalse()
+ assertThat(underTest.setPlayer(playerModel!!)).isFalse()
}
@Test
@@ -120,7 +122,7 @@ class MediaControlViewModelTest : SysuiTestCase() {
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
mediaData = initMediaData(ARTIST_2, TITLE_2)
@@ -129,7 +131,32 @@ class MediaControlViewModelTest : SysuiTestCase() {
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE_2)
assertThat(playerModel?.artistName).isEqualTo(ARTIST_2)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
+ }
+
+ @Test
+ fun reservedButtons_showScrubbingTimes() =
+ testScope.runTest {
+ val playerModel by collectLastValue(underTest.player)
+ val mediaData =
+ initMediaData(ARTIST, TITLE)
+ .copy(semanticActions = MediaButton(reserveNext = true, reservePrev = true))
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel?.actionButtons).isNotNull()
+ assertThat(playerModel!!.useSemanticActions).isTrue()
+ assertThat(playerModel!!.canShowTime).isTrue()
+
+ val buttons = playerModel!!.actionButtons
+
+ val prevButton = buttons.find { it.buttonId == R.id.actionPrev }!!
+ assertThat(prevButton.notVisibleValue).isEqualTo(ConstraintSet.GONE)
+ assertThat(prevButton.isVisibleWhenScrubbing).isEqualTo(false)
+
+ val nextButton = buttons.find { it.buttonId == R.id.actionNext }!!
+ assertThat(nextButton.notVisibleValue).isEqualTo(ConstraintSet.GONE)
+ assertThat(nextButton.isVisibleWhenScrubbing).isEqualTo(false)
}
private fun initMediaData(artist: String, title: String): MediaData {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index d921dde1fa44..a36e0eac086e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -9,6 +9,7 @@ import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.shared.system.TaskStackChangeListeners
import com.android.systemui.statusbar.CommandQueue
@@ -59,6 +60,7 @@ class TaskbarDelegateTest : SysuiTestCase() {
@Mock lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState
@Mock lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock lateinit var mDisplayTracker: DisplayTracker
@Before
fun setup() {
@@ -87,6 +89,7 @@ class TaskbarDelegateTest : SysuiTestCase() {
mOptionalPip,
mBackAnimation,
mTaskStackChangeListeners,
+ mDisplayTracker,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index 53dec696004d..f725e06fd0e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.power.domain.interactor
import android.os.PowerManager
+import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -25,9 +26,12 @@ import com.android.systemui.camera.cameraGestureHelper
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.dozeInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -51,9 +55,9 @@ class PowerInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val cameraGestureHelper = kosmos.cameraGestureHelper
+ private val repository: FakePowerRepository = kosmos.fakePowerRepository
private lateinit var underTest: PowerInteractor
- private lateinit var repository: FakePowerRepository
private val keyguardRepository = FakeKeyguardRepository()
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController
@@ -63,7 +67,6 @@ class PowerInteractorTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- repository = FakePowerRepository()
underTest =
PowerInteractor(
repository,
@@ -208,7 +211,7 @@ class PowerInteractorTest : SysuiTestCase() {
whenever(cameraGestureHelper.canCameraGestureBeLaunched(any())).thenReturn(false)
underTest.onStartedWakingUp(
PowerManager.WAKE_REASON_POWER_BUTTON,
- /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+ /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
)
underTest.onFinishedWakingUp()
underTest.onCameraLaunchGestureDetected()
@@ -224,7 +227,7 @@ class PowerInteractorTest : SysuiTestCase() {
fun onCameraLaunchGestureDetected_maintainsAllOtherState() {
underTest.onStartedWakingUp(
PowerManager.WAKE_REASON_POWER_BUTTON,
- /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+ /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
)
underTest.onFinishedWakingUp()
underTest.onCameraLaunchGestureDetected()
@@ -244,7 +247,7 @@ class PowerInteractorTest : SysuiTestCase() {
underTest.onFinishedGoingToSleep(/* powerButtonLaunchGestureTriggeredDuringSleep= */ false)
underTest.onStartedWakingUp(
PowerManager.WAKE_REASON_POWER_BUTTON,
- /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+ /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
)
underTest.onFinishedWakingUp()
@@ -262,7 +265,7 @@ class PowerInteractorTest : SysuiTestCase() {
// This state should only be reset onStartedGoingToSleep.
underTest.onStartedWakingUp(
PowerManager.WAKE_REASON_POWER_BUTTON,
- /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+ /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
)
underTest.onFinishedWakingUp()
@@ -272,4 +275,26 @@ class PowerInteractorTest : SysuiTestCase() {
.isEqualTo(WakeSleepReason.POWER_BUTTON)
assertTrue(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
}
+
+ @Test
+ fun dozeScreenState() =
+ testScope.runTest {
+ val dozeScreenState by collectLastValue(underTest.dozeScreenState)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.UNKNOWN)
+
+ kosmos.dozeInteractor.setDozeScreenState(Display.STATE_OFF)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.OFF)
+
+ kosmos.dozeInteractor.setDozeScreenState(Display.STATE_ON)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.ON)
+
+ kosmos.dozeInteractor.setDozeScreenState(Display.STATE_DOZE)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.DOZE)
+
+ kosmos.dozeInteractor.setDozeScreenState(Display.STATE_DOZE_SUSPEND)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.DOZE_SUSPEND)
+
+ kosmos.dozeInteractor.setDozeScreenState(Display.STATE_ON_SUSPEND)
+ assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.ON_SUSPEND)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 0356422bda04..004aeb069a14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -122,7 +122,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
mSystemClock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(mSystemClock);
mBackgroundExecutor = new FakeExecutor(mSystemClock);
- when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
+ when(mSystemUIDialogFactory.create(eq(mContext))).thenReturn(mSystemUIDialog);
mUserProfiles = new ArrayList<>();
Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
@@ -346,6 +346,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"true", false);
FgsManagerController fmc = new FgsManagerControllerImpl(
+ mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -373,6 +374,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"false", false);
fmc = new FgsManagerControllerImpl(
+ mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -485,6 +487,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(BroadcastReceiver.class);
FgsManagerController result = new FgsManagerControllerImpl(
+ mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 111b3b65c05d..4457d9b25ade 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.qs.fgsManagerController
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.setConfigurationForMediaInRow
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
@@ -436,6 +437,28 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
}
}
+ @Test
+ fun qsVisibleAndAnyShadeVisible() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isQsVisible = false
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = true
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = false
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = true
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isTrue()
+ }
+ }
+
private fun TestScope.setMediaState(state: MediaState) {
with(kosmos) {
val activeMedia = state == ACTIVE_MEDIA
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 09a6c2c7f1f7..1899b7d1f1bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -44,6 +44,7 @@ import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArraySet;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -384,6 +385,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
return mSpec;
}
+ @NonNull
@Override
public State getState() {
return mState;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt
new file mode 100644
index 000000000000..eef195b56188
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.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.qs.panels.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridLayoutTypeRepositoryTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+
+ val underTest = kosmos.gridLayoutTypeRepository
+
+ @Test
+ fun defaultType_paginated() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.defaultLayoutType)
+
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+ }
+
+ @Test
+ fun dualShadeType_infinite() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.dualShadeLayoutType)
+
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
index 56cead19d1df..0d2f4eec370d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
@@ -17,8 +17,11 @@
package com.android.systemui.qs.panels.data.repository
import android.content.res.mainResources
+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.server.display.feature.flags.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
@@ -30,12 +33,49 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SmallTest
class StockTilesRepositoryTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
- private val underTest = StockTilesRepository(kosmos.mainResources)
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ fun stockTilesMatchesResources_evenDimmerFlagOn_configOn() {
+ // Enable the EvenDimmer config
+ mContext
+ .getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.bool.config_evenDimmerEnabled, true)
+ val underTest = StockTilesRepository(kosmos.mainResources)
+
+ val expected =
+ kosmos.mainResources
+ .getString(R.string.quick_settings_tiles_stock)
+ .split(",")
+ .filterNot { it.equals("reduce_brightness") }
+ .map(TileSpec::create)
+ assertThat(underTest.stockTiles).isEqualTo(expected)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ fun stockTilesMatchesResources_evenDimmerFlagOn_configOff() {
+ // Disable the EvenDimmer config
+ mContext
+ .getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.bool.config_evenDimmerEnabled, false)
+ val underTest = StockTilesRepository(kosmos.mainResources)
+
+ val expected =
+ kosmos.mainResources
+ .getString(R.string.quick_settings_tiles_stock)
+ .split(",")
+ .map(TileSpec::create)
+ assertThat(underTest.stockTiles).isEqualTo(expected)
+ }
@Test
- fun stockTilesMatchesResources() {
+ @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+ fun stockTilesMatchesResources_evenDimmerFlagOff() {
+ val underTest = StockTilesRepository(kosmos.mainResources)
+
val expected =
kosmos.mainResources
.getString(R.string.quick_settings_tiles_stock)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
new file mode 100644
index 000000000000..b5915386b443
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.panels.domain.interactor
+
+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.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridLayoutTypeInteractorTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+
+ val Kosmos.underTest by Kosmos.Fixture { kosmos.gridLayoutTypeInteractor }
+
+ @DisableFlags(DualShade.FLAG_NAME)
+ @Test
+ fun noDualShade_gridAlwaysPaginated() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.layout)
+
+ fakeShadeRepository.setShadeLayoutWide(false)
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+
+ fakeShadeRepository.setShadeLayoutWide(true)
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+ }
+
+ @EnableFlags(DualShade.FLAG_NAME)
+ @Test
+ fun dualShade_gridAlwaysInfinite() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.layout)
+
+ fakeShadeRepository.setShadeLayoutWide(false)
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+
+ fakeShadeRepository.setShadeLayoutWide(true)
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+ }
+}
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 ae7c44e9b146..8b9ae9a0606d 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
@@ -39,7 +39,7 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun startDrag_listHasSpacers() {
- underTest.onStarted(TestEditTiles[0])
+ underTest.onStarted(TestEditTiles[0], DragType.Add)
// [ a ] [ b ] [ c ] [ X ]
// [ Large D ] [ e ] [ X ]
@@ -51,8 +51,8 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun moveDrag_listChanges() {
- underTest.onStarted(TestEditTiles[4])
- underTest.onMoved(3, false)
+ underTest.onStarted(TestEditTiles[4], DragType.Add)
+ underTest.onTargeting(3, false)
// Tile E goes to index 3
// [ a ] [ b ] [ c ] [ e ]
@@ -65,8 +65,8 @@ class EditTileListStateTest : SysuiTestCase() {
fun moveDragOnSidesOfLargeTile_listChanges() {
val draggedCell = TestEditTiles[4]
- underTest.onStarted(draggedCell)
- underTest.onMoved(4, true)
+ underTest.onStarted(draggedCell, DragType.Add)
+ underTest.onTargeting(4, true)
// Tile E goes to the right side of tile D, list is unchanged
// [ a ] [ b ] [ c ] [ X ]
@@ -74,7 +74,7 @@ class EditTileListStateTest : SysuiTestCase() {
assertThat(underTest.tiles.toStrings())
.isEqualTo(listOf("a", "b", "c", "spacer", "d", "e", "spacer"))
- underTest.onMoved(4, false)
+ underTest.onTargeting(4, false)
// Tile E goes to the left side of tile D, they swap positions
// [ a ] [ b ] [ c ] [ e ]
@@ -87,8 +87,8 @@ class EditTileListStateTest : SysuiTestCase() {
fun moveNewTile_tileIsAdded() {
val newTile = createEditTile("newTile", 2)
- underTest.onStarted(newTile)
- underTest.onMoved(5, false)
+ underTest.onStarted(newTile, DragType.Add)
+ underTest.onTargeting(5, false)
// New tile goes to index 5
// [ a ] [ b ] [ c ] [ X ]
@@ -102,7 +102,7 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun movedTileOutOfBounds_tileDisappears() {
- underTest.onStarted(TestEditTiles[0])
+ underTest.onStarted(TestEditTiles[0], DragType.Add)
underTest.movedOutOfBounds()
assertThat(underTest.tiles.toStrings()).doesNotContain(TestEditTiles[0].tile.tileSpec.spec)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
index a9a527fb8df6..4891c9fb6def 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
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 583db722a759..bbfa7e7a81ee 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
@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.graphics.drawable.TestStubDrawable
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
@@ -29,9 +30,12 @@ import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FakeQSFactory
import com.android.systemui.qs.FakeQSTile
+import com.android.systemui.qs.QSEditEvent
import com.android.systemui.qs.panels.data.repository.stockTilesRepository
import com.android.systemui.qs.panels.domain.interactor.FakeTileAvailabilityInteractor
import com.android.systemui.qs.panels.domain.interactor.tileAvailabilityInteractorsMap
@@ -42,8 +46,10 @@ import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesReposi
import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
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.impl.airplane.qsAirplaneModeTileConfig
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
@@ -86,6 +92,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
qsFlashlightTileConfig,
qsBatterySaverTileConfig,
qsAlarmTileConfig,
+ qsAirplaneModeTileConfig,
qsCameraSensorPrivacyToggleTileConfig,
qsMicrophoneSensorPrivacyToggleTileConfig,
)
@@ -116,7 +123,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
fakeInstalledTilesRepository.setInstalledServicesForUser(
userTracker.userId,
- listOf(serviceInfo1, serviceInfo2)
+ listOf(serviceInfo1, serviceInfo2),
)
with(fakeQSTileConfigProvider) { configs.forEach { putConfig(it.tileSpec, it) } }
@@ -424,10 +431,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
testScope.runTest {
val tiles by collectLastValue(underTest.tiles)
val currentTiles =
- mutableListOf(
- TileSpec.create("flashlight"),
- TileSpec.create("airplane"),
- )
+ mutableListOf(TileSpec.create("flashlight"), TileSpec.create("airplane"))
currentTilesInteractor.setTiles(currentTiles)
assertThat(currentTiles.size).isLessThan(minNumberOfTiles)
@@ -549,6 +553,156 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
}
+ // UI EVENT TESTS
+
+ @Test
+ fun startEditing_onlyOneEvent() =
+ kosmos.runTest {
+ underTest.startEditing()
+ underTest.startEditing()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ assertThat(uiEventLoggerFake[0].eventId).isEqualTo(QSEditEvent.QS_EDIT_OPEN.id)
+ }
+
+ @Test
+ fun stopEditing_notEditing_noEvent() =
+ kosmos.runTest {
+ underTest.stopEditing()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun stopEditing_whenEditing_correctEvent() =
+ kosmos.runTest {
+ underTest.startEditing()
+ underTest.stopEditing()
+
+ assertThat(uiEventLoggerFake[1].eventId).isEqualTo(QSEditEvent.QS_EDIT_CLOSED.id)
+ }
+
+ @Test
+ fun addTile_correctPackageAndPosition() =
+ kosmos.runTest {
+ val flashlightTile = TileSpec.create("flashlight")
+ val airplaneTile = TileSpec.create("airplane")
+ val internetTile = TileSpec.create("internet")
+ val customTile = TileSpec.create(component2)
+ currentTilesInteractor.setTiles(listOf(flashlightTile))
+ runCurrent()
+
+ underTest.addTile(airplaneTile)
+ underTest.addTile(internetTile, position = 0)
+ underTest.addTile(customTile, position = 1)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(3)
+
+ with(uiEventLoggerFake[0]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_ADD.id)
+ assertThat(packageName).isEqualTo(airplaneTile.metricSpec)
+ assertThat(position).isEqualTo(-1)
+ }
+ with(uiEventLoggerFake[1]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_ADD.id)
+ assertThat(packageName).isEqualTo(internetTile.metricSpec)
+ assertThat(position).isEqualTo(0)
+ }
+ with(uiEventLoggerFake[2]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_ADD.id)
+ assertThat(packageName).isEqualTo(customTile.metricSpec)
+ assertThat(position).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun addTile_alreadyThere_usesMoveEvent() =
+ kosmos.runTest {
+ val flashlightTile = TileSpec.create("flashlight")
+ val airplaneTile = TileSpec.create("airplane")
+ val internetTile = TileSpec.create("internet")
+ currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
+ runCurrent()
+
+ underTest.addTile(flashlightTile) // adding at the end, should use correct position
+ underTest.addTile(internetTile, 0)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
+
+ with(uiEventLoggerFake[0]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_MOVE.id)
+ assertThat(packageName).isEqualTo(flashlightTile.metricSpec)
+ // adding at the end, should use correct position
+ assertThat(position).isEqualTo(2)
+ }
+ with(uiEventLoggerFake[1]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_MOVE.id)
+ assertThat(packageName).isEqualTo(internetTile.metricSpec)
+ assertThat(position).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun removeTileEvent() =
+ kosmos.runTest {
+ val flashlightTile = TileSpec.create("flashlight")
+ val airplaneTile = TileSpec.create("airplane")
+ val internetTile = TileSpec.create("internet")
+ currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
+ runCurrent()
+
+ underTest.removeTile(airplaneTile)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ with(uiEventLoggerFake[0]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_REMOVE.id)
+ assertThat(packageName).isEqualTo(airplaneTile.metricSpec)
+ }
+ }
+
+ @Test
+ fun setTiles_emitsCorrectOperation_individualOperations() =
+ kosmos.runTest {
+ val flashlightTile = TileSpec.create("flashlight")
+ val airplaneTile = TileSpec.create("airplane")
+ val internetTile = TileSpec.create("internet")
+ val alarmTile = TileSpec.create("alarm")
+
+ currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
+ runCurrent()
+
+ // 0. Move flashlightTile to position 2
+ underTest.setTiles(listOf(airplaneTile, internetTile, flashlightTile))
+ runCurrent()
+
+ // 1. Add alarm tile at position 1
+ underTest.setTiles(listOf(airplaneTile, alarmTile, internetTile, flashlightTile))
+ runCurrent()
+
+ // 2. Remove internetTile
+ underTest.setTiles(listOf(airplaneTile, alarmTile, flashlightTile))
+ runCurrent()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(3)
+
+ with(uiEventLoggerFake[0]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_MOVE.id)
+ assertThat(packageName).isEqualTo(flashlightTile.metricSpec)
+ assertThat(position).isEqualTo(2)
+ }
+ with(uiEventLoggerFake[1]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_ADD.id)
+ assertThat(packageName).isEqualTo(alarmTile.metricSpec)
+ assertThat(position).isEqualTo(1)
+ }
+ with(uiEventLoggerFake[2]) {
+ assertThat(eventId).isEqualTo(QSEditEvent.QS_EDIT_REMOVE.id)
+ assertThat(packageName).isEqualTo(internetTile.metricSpec)
+ }
+ }
+
companion object {
private val drawable1 = TestStubDrawable("drawable1")
private val appName1 = "App1"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
index f2bfd729f74a..a8e390c25a4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.classifier.fakeFalsingManager
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.editModeButtonViewModelFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
index 869ab6c24fce..1fc1c0fd1e6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
@@ -85,6 +85,24 @@ class TileSpecTest : SysuiTestCase() {
assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
}
+ @Test
+ fun metricSpec_invalid() {
+ assertThat(TileSpec.Invalid.metricSpec).isEmpty()
+ }
+
+ @Test
+ fun metricSpec_platform_specName() {
+ val tile = "spec"
+ assertThat(TileSpec.create(tile).metricSpec).isEqualTo(tile)
+ }
+
+ @Test
+ fun metricSpec_custom_packageName() {
+ val componentName = ComponentName("test_pkg", "test_cls")
+
+ assertThat(TileSpec.create(componentName).metricSpec).isEqualTo(componentName.packageName)
+ }
+
companion object {
private const val CUSTOM_TILE_PREFIX = "custom("
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index 7a99aefc98fe..b921ff7063a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -18,8 +18,9 @@ package com.android.systemui.qs.tiles
import android.net.ConnectivityManager
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.telephony.flags.Flags
@@ -31,8 +32,11 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.settings.GlobalSettings
@@ -49,39 +53,34 @@ import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class AirplaneModeTileTest : SysuiTestCase() {
-
- @Mock
- private lateinit var mHost: QSHost
- @Mock
- private lateinit var mMetricsLogger: MetricsLogger
- @Mock
- private lateinit var mStatusBarStateController: StatusBarStateController
- @Mock
- private lateinit var mActivityStarter: ActivityStarter
- @Mock
- private lateinit var mQsLogger: QSLogger
- @Mock
- private lateinit var mBroadcastDispatcher: BroadcastDispatcher
- @Mock
- private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
- @Mock
- private lateinit var mConnectivityManager: ConnectivityManager
- @Mock
- private lateinit var mGlobalSettings: GlobalSettings
- @Mock
- private lateinit var mUserTracker: UserTracker
- @Mock
- private lateinit var mUiEventLogger: QsEventLogger
+class AirplaneModeTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var mHost: QSHost
+ @Mock private lateinit var mMetricsLogger: MetricsLogger
+ @Mock private lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock private lateinit var mActivityStarter: ActivityStarter
+ @Mock private lateinit var mQsLogger: QSLogger
+ @Mock private lateinit var mBroadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
+ @Mock private lateinit var mConnectivityManager: ConnectivityManager
+ @Mock private lateinit var mGlobalSettings: GlobalSettings
+ @Mock private lateinit var mUserTracker: UserTracker
+ @Mock private lateinit var mUiEventLogger: QsEventLogger
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: AirplaneModeTile
- @Mock
- private lateinit var mClickJob: Job
+ @Mock private lateinit var mClickJob: Job
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -89,20 +88,22 @@ class AirplaneModeTileTest : SysuiTestCase() {
Mockito.`when`(mHost.context).thenReturn(mContext)
Mockito.`when`(mHost.userContext).thenReturn(mContext)
Mockito.`when`(mLazyConnectivityManager.get()).thenReturn(mConnectivityManager)
- mTile = AirplaneModeTile(
- mHost,
- mUiEventLogger,
- mTestableLooper.looper,
- Handler(mTestableLooper.looper),
- FalsingManagerFake(),
- mMetricsLogger,
- mStatusBarStateController,
- mActivityStarter,
- mQsLogger,
- mBroadcastDispatcher,
- mLazyConnectivityManager,
- mGlobalSettings,
- mUserTracker)
+ mTile =
+ AirplaneModeTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.looper,
+ Handler(mTestableLooper.looper),
+ FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger,
+ mBroadcastDispatcher,
+ mLazyConnectivityManager,
+ mGlobalSettings,
+ mUserTracker,
+ )
}
@After
@@ -117,8 +118,7 @@ class AirplaneModeTileTest : SysuiTestCase() {
mTile.handleUpdateState(state, 0)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_airplane_icon_off))
}
@Test
@@ -127,8 +127,7 @@ class AirplaneModeTileTest : SysuiTestCase() {
mTile.handleUpdateState(state, 1)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_airplane_icon_on))
}
@Test
@@ -150,4 +149,20 @@ class AirplaneModeTileTest : SysuiTestCase() {
verify(mConnectivityManager, times(0)).setAirplaneMode(any())
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index d6be31450fc0..cf9ef4d7c2a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -18,9 +18,10 @@ package com.android.systemui.qs.tiles
import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -31,8 +32,11 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.settings.FakeSettings
@@ -49,14 +53,26 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-class BatterySaverTileTest : SysuiTestCase() {
+class BatterySaverTileTest(flagsParameterization: FlagsParameterization) : SysuiTestCase() {
companion object {
private const val USER = 10
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flagsParameterization)
}
@Mock private lateinit var userContext: Context
@@ -94,7 +110,7 @@ class BatterySaverTileTest : SysuiTestCase() {
activityStarter,
qsLogger,
batteryController,
- secureSettings
+ secureSettings,
)
tile.initialize()
@@ -150,8 +166,7 @@ class BatterySaverTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_battery_saver_icon_off))
}
@Test
@@ -161,7 +176,14 @@ class BatterySaverTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_battery_saver_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 093cdf22a64b..6bb671315640 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.safetycenter.SafetyCenterManager
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -31,8 +31,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLoggerFake
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.google.common.truth.Truth.assertThat
@@ -41,37 +45,40 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class CameraToggleTileTest : SysuiTestCase() {
+class CameraToggleTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
/* isBlocked */
const val CAMERA_TOGGLE_ENABLED: Boolean = false
const val CAMERA_TOGGLE_DISABLED: Boolean = true
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
- @Mock
- private lateinit var host: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var privacyController: IndividualSensorPrivacyController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var uiEventLogger: QsEventLoggerFake
- @Mock
- private lateinit var safetyCenterManager: SafetyCenterManager
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var uiEventLogger: QsEventLoggerFake
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
private lateinit var testableLooper: TestableLooper
private lateinit var tile: CameraToggleTile
@@ -82,7 +89,8 @@ class CameraToggleTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- tile = CameraToggleTile(
+ tile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -94,7 +102,8 @@ class CameraToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
}
@After
@@ -109,8 +118,7 @@ class CameraToggleTileTest : SysuiTestCase() {
tile.handleUpdateState(state, CAMERA_TOGGLE_ENABLED)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_camera_access_icon_on))
}
@Test
@@ -119,14 +127,14 @@ class CameraToggleTileTest : SysuiTestCase() {
tile.handleUpdateState(state, CAMERA_TOGGLE_DISABLED)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_camera_access_icon_off))
}
@Test
fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- val cameraTile = CameraToggleTile(
+ val cameraTile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -138,7 +146,8 @@ class CameraToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(cameraTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
cameraTile.destroy()
testableLooper.processAllMessages()
@@ -147,7 +156,8 @@ class CameraToggleTileTest : SysuiTestCase() {
@Test
fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
- val cameraTile = CameraToggleTile(
+ val cameraTile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -159,9 +169,18 @@ class CameraToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
cameraTile.destroy()
testableLooper.processAllMessages()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index 1343527e631b..a58dd6301e07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,10 +29,10 @@ import static org.mockito.Mockito.when;
import android.content.Intent;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.provider.Settings;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -39,6 +43,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -54,13 +59,23 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ColorInversionTileTest extends SysuiTestCase {
private static final Integer COLOR_INVERSION_DISABLED = 0;
private static final Integer COLOR_INVERSION_ENABLED = 1;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -80,6 +95,11 @@ public class ColorInversionTileTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
private ColorInversionTile mTile;
+ public ColorInversionTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -133,7 +153,7 @@ public class ColorInversionTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, COLOR_INVERSION_DISABLED);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_off));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_invert_colors_icon_off));
}
@Test
@@ -143,6 +163,14 @@ public class ColorInversionTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, COLOR_INVERSION_ENABLED);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_on));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_invert_colors_icon_on));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 73ae4ee5aa0d..fbbdc46a7873 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -17,8 +17,9 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -29,8 +30,11 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
@@ -42,11 +46,18 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class DataSaverTileTest : SysuiTestCase() {
+class DataSaverTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var mHost: QSHost
@Mock private lateinit var mMetricsLogger: MetricsLogger
@@ -69,7 +80,7 @@ class DataSaverTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
whenever(mHost.context).thenReturn(mContext)
- whenever(systemUIDialogFactory.create()).thenReturn(systemUIDialog)
+ whenever(systemUIDialogFactory.create(eq(mContext))).thenReturn(systemUIDialog)
tile =
DataSaverTile(
@@ -84,7 +95,7 @@ class DataSaverTileTest : SysuiTestCase() {
mQsLogger,
dataSaverController,
mDialogTransitionAnimator,
- systemUIDialogFactory
+ systemUIDialogFactory,
)
}
@@ -100,8 +111,7 @@ class DataSaverTileTest : SysuiTestCase() {
tile.handleUpdateState(state, true)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_data_saver_icon_on))
}
@Test
@@ -110,7 +120,22 @@ class DataSaverTileTest : SysuiTestCase() {
tile.handleUpdateState(state, false)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_data_saver_icon_off))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 33748b973f1c..0bb86da5d955 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -20,11 +20,14 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import androidx.lifecycle.LifecycleOwner
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -43,7 +46,9 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
@@ -67,11 +72,17 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class DeviceControlsTileTest : SysuiTestCase() {
+class DeviceControlsTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
@@ -130,7 +141,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
if (featureEnabled) {
Optional.of(controlsController)
} else {
- Optional.empty()
+ Optional.empty<ControlsController>()
}
}
@@ -138,7 +149,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
if (featureEnabled) {
Optional.of(controlsListingController)
} else {
- Optional.empty()
+ Optional.empty<ControlsController>()
}
}
@@ -146,12 +157,12 @@ class DeviceControlsTileTest : SysuiTestCase() {
if (featureEnabled) {
Optional.of(controlsUiController)
} else {
- Optional.empty()
+ Optional.empty<ControlsController>()
}
}
`when`(controlsComponent.getTileTitleId()).thenReturn(R.string.quick_controls_title)
- `when`(controlsComponent.getTileTitleId()).thenReturn(R.drawable.controls_icon)
+ `when`(controlsComponent.getTileImageId()).thenReturn(R.drawable.controls_icon)
}
@Test
@@ -378,6 +389,28 @@ class DeviceControlsTileTest : SysuiTestCase() {
assertThat(tile.tileLabel).isEqualTo(context.getText(controlsComponent.getTileTitleId()))
}
+ @Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ fun tileIconEqualsResourceFromComponent_composeFlagDisabled() {
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ assertThat(tile.state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.controls_icon))
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ fun tileIconEqualsResourceFromComponent_composeFlagEnable() {
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ assertThat(tile.state.icon)
+ .isEqualTo(
+ QSTileImpl.DrawableIconWithRes(
+ mContext.getDrawable(R.drawable.controls_icon),
+ R.drawable.controls_icon,
+ )
+ )
+ }
+
private fun createTile(): DeviceControlsTile {
return DeviceControlsTile(
qsHost,
@@ -396,6 +429,14 @@ class DeviceControlsTileTest : SysuiTestCase() {
testableLooper.processAllMessages()
}
}
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
private const val CONTROLS_ACTIVITY_CLASS_NAME = "com.android.systemui.controls.ui.ControlsActivity"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index f90463e7f589..6a15a5bc21e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -1,12 +1,11 @@
package com.android.systemui.qs.tiles
-import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -14,8 +13,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.FlashlightController
import com.google.common.truth.Truth
import org.junit.After
@@ -25,13 +28,17 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class FlashlightTileTest : SysuiTestCase() {
+class FlashlightTileTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock private lateinit var mockContext: Context
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsLogger: QSLogger
@@ -56,7 +63,7 @@ class FlashlightTileTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- Mockito.`when`(qsHost.context).thenReturn(mockContext)
+ Mockito.`when`(qsHost.context).thenReturn(mContext)
tile =
FlashlightTile(
@@ -69,7 +76,7 @@ class FlashlightTileTest : SysuiTestCase() {
statusBarStateController,
activityStarter,
qsLogger,
- flashlightController
+ flashlightController,
)
}
@@ -87,8 +94,7 @@ class FlashlightTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_on))
+ Truth.assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_on))
}
@Test
@@ -100,7 +106,7 @@ class FlashlightTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_off))
}
@Test
@@ -111,6 +117,22 @@ class FlashlightTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_off))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index f33de4d9144d..eeccbdf20540 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -17,10 +17,11 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -29,6 +30,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
@@ -62,12 +64,18 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class InternetTileNewImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class InternetTileNewImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
lateinit var underTest: InternetTileNewImpl
private val testDispatcher = StandardTestDispatcher()
@@ -252,5 +260,11 @@ class InternetTileNewImplTest : SysuiTestCase() {
const val WIFI_SSID = "test ssid"
val ACTIVE_WIFI =
WifiNetworkModel.Active.of(isValidated = true, level = 4, ssid = WIFI_SSID)
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index b5ec0a022dd0..d7b183ed7def 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -25,19 +29,21 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.dialog.InternetDialogManager;
@@ -55,11 +61,21 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class InternetTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -76,6 +92,11 @@ public class InternetTileTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private InternetTile mTile;
+ public InternetTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -172,7 +193,7 @@ public class InternetTileTest extends SysuiTestCase {
mTile.mSignalCallback.setIsAirplaneMode(state);
mTestableLooper.processAllMessages();
assertThat(mTile.getState().icon).isEqualTo(
- QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable));
+ createExpectedIcon(R.drawable.ic_qs_no_internet_unavailable));
}
@Test
@@ -194,4 +215,12 @@ public class InternetTileTest extends SysuiTestCase {
verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(true));
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 0a1455fe12cc..a581b57ac44f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -16,13 +16,12 @@
package com.android.systemui.qs.tiles
-import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -30,9 +29,13 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
import com.android.systemui.util.mockito.argumentCaptor
@@ -46,33 +49,28 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class LocationTileTest : SysuiTestCase() {
-
- @Mock
- private lateinit var mockContext: Context
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var qsHost: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
+class LocationTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var qsHost: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
private val falsingManager = FalsingManagerFake()
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var locationController: LocationController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var panelInteractor: PanelInteractor
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var locationController: LocationController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var panelInteractor: PanelInteractor
+ @Mock private lateinit var uiEventLogger: QsEventLogger
private lateinit var testableLooper: TestableLooper
private lateinit var tile: LocationTile
@@ -81,22 +79,23 @@ class LocationTileTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- `when`(qsHost.context).thenReturn(mockContext)
-
- tile = LocationTile(
- qsHost,
- uiEventLogger,
- testableLooper.looper,
- Handler(testableLooper.looper),
- falsingManager,
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger,
- locationController,
- keyguardStateController,
- panelInteractor,
- )
+ `when`(qsHost.context).thenReturn(mContext)
+
+ tile =
+ LocationTile(
+ qsHost,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ locationController,
+ keyguardStateController,
+ panelInteractor,
+ )
}
@After
@@ -112,8 +111,7 @@ class LocationTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_location_icon_off))
}
@Test
@@ -123,8 +121,7 @@ class LocationTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_location_icon_on))
}
@Test
@@ -140,4 +137,20 @@ class LocationTileTest : SysuiTestCase() {
verify(panelInteractor).openPanels()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index dbdf3a499f8b..a39692d10863 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.safetycenter.SafetyCenterManager
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -31,8 +31,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.google.common.truth.Truth.assertThat
@@ -41,49 +45,52 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class MicrophoneToggleTileTest : SysuiTestCase() {
+class MicrophoneToggleTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
/* isBlocked */
const val MICROPHONE_TOGGLE_ENABLED: Boolean = false
const val MICROPHONE_TOGGLE_DISABLED: Boolean = true
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
- @Mock
- private lateinit var host: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var privacyController: IndividualSensorPrivacyController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
- @Mock
- private lateinit var safetyCenterManager: SafetyCenterManager
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
private lateinit var testableLooper: TestableLooper
private lateinit var tile: MicrophoneToggleTile
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- tile = MicrophoneToggleTile(
+ tile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -95,7 +102,8 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
}
@After
@@ -110,7 +118,7 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
tile.handleUpdateState(state, MICROPHONE_TOGGLE_ENABLED)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_mic_access_on))
}
@Test
@@ -119,13 +127,14 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
tile.handleUpdateState(state, MICROPHONE_TOGGLE_DISABLED)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_mic_access_off))
}
@Test
fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- val micTile = MicrophoneToggleTile(
+ val micTile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -137,7 +146,8 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
micTile.destroy()
testableLooper.processAllMessages()
@@ -146,7 +156,8 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
@Test
fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
- val micTile = MicrophoneToggleTile(
+ val micTile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -158,9 +169,18 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
micTile.destroy()
testableLooper.processAllMessages()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
}
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 848c8db4b99d..9173ac969324 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
@@ -94,11 +94,7 @@ class ModesTileTest : SysuiTestCase() {
private val zenModeRepository = kosmos.zenModeRepository
private val tileDataInteractor =
ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
- private val mapper =
- ModesTileMapper(
- context.resources,
- context.theme,
- )
+ private val mapper = ModesTileMapper(context.resources, context.theme)
private lateinit var userActionInteractor: ModesTileUserActionInteractor
private lateinit var secureSettings: SecureSettings
@@ -127,10 +123,7 @@ class ModesTileTest : SysuiTestCase() {
)
userActionInteractor =
- ModesTileUserActionInteractor(
- inputHandler,
- dialogDelegate,
- )
+ ModesTileUserActionInteractor(inputHandler, dialogDelegate, kosmos.zenModeInteractor)
underTest =
ModesTile(
@@ -185,7 +178,7 @@ class ModesTileTest : SysuiTestCase() {
ModesTileModel(
isActivated = true,
activeModes = listOf("One", "Two"),
- icon = TestStubDrawable().asIcon()
+ icon = TestStubDrawable().asIcon(),
)
underTest.handleUpdateState(tileState, model)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index f1c589512895..a193cbcec114 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -19,11 +19,11 @@ package com.android.systemui.qs.tiles
import android.hardware.display.ColorDisplayManager
import android.hardware.display.NightDisplayListener
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dagger.NightDisplayListenerModule
@@ -32,8 +32,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.LocationController
import com.google.common.truth.Truth
import org.junit.After
@@ -42,13 +46,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class NightDisplayTileTest : SysuiTestCase() {
+class NightDisplayTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Mock private lateinit var mHost: QSHost
@Mock private lateinit var mMetricsLogger: MetricsLogger
@@ -72,8 +83,6 @@ class NightDisplayTileTest : SysuiTestCase() {
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: NightDisplayTile
-
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -97,7 +106,7 @@ class NightDisplayTileTest : SysuiTestCase() {
mQsLogger,
mLocationController,
mColorDisplayManager,
- mNightDisplayListenerBuilder
+ mNightDisplayListenerBuilder,
)
}
@@ -115,7 +124,7 @@ class NightDisplayTileTest : SysuiTestCase() {
mTile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_nightlight_icon_off))
}
@Test
@@ -125,7 +134,22 @@ class NightDisplayTileTest : SysuiTestCase() {
mTile.handleUpdateState(state, /* arg= */ null)
- Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_on))
+ Truth.assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_nightlight_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index f8f82f2c2ed8..682daea2cb1f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -112,7 +113,7 @@ public class QRCodeScannerTileTest extends SysuiTestCase {
assertEquals(state.label, mContext.getString(R.string.qr_code_scanner_title));
assertEquals(state.contentDescription, mContext.getString(R.string.qr_code_scanner_title));
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.ic_qr_code_scanner));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.ic_qr_code_scanner));
}
@Test
@@ -133,4 +134,12 @@ public class QRCodeScannerTileTest extends SysuiTestCase {
assertEquals(state.state, Tile.STATE_INACTIVE);
assertNull(state.secondaryLabel);
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 4068d9fd7f3d..33951672d05b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -294,7 +295,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
public void testHandleUpdateState_updateLabelAndIcon_noIconFromApi() {
when(mQuickAccessWalletClient.getTileIcon()).thenReturn(null);
QSTile.State state = new QSTile.State();
- QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
+ QSTile.Icon icon = createExpectedIcon(R.drawable.ic_wallet_lockscreen);
mTile.handleUpdateState(state, null);
@@ -574,5 +575,13 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
CARD_ID, INVALID_CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 1aff45bf581d..c24498411ff7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -28,10 +32,10 @@ import static org.mockito.Mockito.when;
import android.os.Handler;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -46,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.ReduceBrightColorsController;
+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.res.R.drawable;
@@ -58,10 +63,21 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ReduceBrightColorsTileTest extends SysuiTestCase {
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -84,6 +100,11 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private ReduceBrightColorsTile mTile;
+ public ReduceBrightColorsTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -234,7 +255,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_on));
+ assertEquals(state.icon, createExpectedIcon(drawable.qs_extra_dim_icon_on));
}
@Test
@@ -245,7 +266,15 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_off));
+ assertEquals(state.icon, createExpectedIcon(drawable.qs_extra_dim_icon_off));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 41930636cfa3..fee358a7c15d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -17,6 +17,9 @@
package com.android.systemui.qs.tiles;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
import static junit.framework.TestCase.assertEquals;
@@ -27,10 +30,10 @@ import android.Manifest;
import android.content.pm.PackageManager;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.testing.TestableResources;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -41,6 +44,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -58,7 +62,12 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class RotationLockTileTest extends SysuiTestCase {
@@ -69,6 +78,11 @@ public class RotationLockTileTest extends SysuiTestCase {
"1:2"
};
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private PackageManager mPackageManager;
@Mock
@@ -97,6 +111,11 @@ public class RotationLockTileTest extends SysuiTestCase {
private RotationLockTile mLockTile;
private TestableResources mTestableResources;
+ public RotationLockTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -247,7 +266,7 @@ public class RotationLockTileTest extends SysuiTestCase {
mLockTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_auto_rotate_icon_off));
}
@Test
@@ -257,7 +276,7 @@ public class RotationLockTileTest extends SysuiTestCase {
mLockTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_auto_rotate_icon_on));
}
@@ -281,4 +300,12 @@ public class RotationLockTileTest extends SysuiTestCase {
private void disableCameraBasedRotation() {
when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(false);
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 6ebe8309bf69..fc1d73b62abd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -31,10 +35,10 @@ import static org.mockito.Mockito.when;
import android.app.Dialog;
import android.media.projection.StopReason;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -48,6 +52,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -65,11 +70,21 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ScreenRecordTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private RecordingController mController;
@Mock
@@ -104,6 +119,11 @@ public class ScreenRecordTileTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
+ public ScreenRecordTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -268,7 +288,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_on));
}
@Test
@@ -279,7 +299,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_on));
}
@Test
@@ -290,7 +310,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_off));
}
@Test
@@ -316,4 +336,12 @@ public class ScreenRecordTileTest extends SysuiTestCase {
.notifyPermissionRequestDisplayed(mContext.getUserId());
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 8324a7303cff..3246e6490799 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -17,15 +17,13 @@
package com.android.systemui.qs.tiles
import android.app.UiModeManager
-import android.content.Context
import android.content.res.Configuration
-import android.content.res.Resources
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -33,8 +31,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.LocationController
@@ -46,15 +48,19 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class UiModeNightTileTest : SysuiTestCase() {
+class UiModeNightTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
- @Mock private lateinit var mockContext: Context
@Mock private lateinit var uiModeManager: UiModeManager
- @Mock private lateinit var resources: Resources
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
@@ -68,19 +74,19 @@ class UiModeNightTileTest : SysuiTestCase() {
private val falsingManager = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: UiModeNightTile
- private lateinit var configuration: Configuration
+ private val configuration = Configuration()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ val initialConfiguration = mContext.resources.configuration
+ onTeardown { mContext.resources.configuration.updateFrom(initialConfiguration) }
+
testableLooper = TestableLooper.get(this)
- configuration = Configuration()
mContext.addMockSystemService(UiModeManager::class.java, uiModeManager)
- `when`(qsHost.context).thenReturn(mockContext)
+ `when`(qsHost.context).thenReturn(mContext)
`when`(qsHost.userContext).thenReturn(mContext)
- `when`(mockContext.resources).thenReturn(resources)
- `when`(resources.configuration).thenReturn(configuration)
tile =
UiModeNightTile(
@@ -95,7 +101,7 @@ class UiModeNightTileTest : SysuiTestCase() {
qsLogger,
configurationController,
batteryController,
- locationController
+ locationController,
)
}
@@ -112,28 +118,45 @@ class UiModeNightTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_light_dark_theme_icon_on))
}
@Test
- fun testIcon_whenNightModeOn_isOffState() {
+ fun testIcon_whenNightModeOff_isOffState() {
val state = QSTile.BooleanState()
setNightModeOff()
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_light_dark_theme_icon_off))
}
private fun setNightModeOn() {
`when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_YES)
configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+ mContext.resources.configuration.updateFrom(configuration)
}
private fun setNightModeOff() {
`when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_NO)
configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+ mContext.resources.configuration.updateFrom(configuration)
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
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 52c476ec92cc..e4a988860a6e 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
@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -136,4 +137,13 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
}
+
+ @Test
+ fun detailsViewModel() =
+ kosmos.testScope.runTest {
+ assertThat(underTest.detailsViewModel.getTitle())
+ .isEqualTo("Internet")
+ assertThat(underTest.detailsViewModel.getSubTitle())
+ .isEqualTo("Tab a network to connect")
+ }
}
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 cd5812710292..88b00468573f 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
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import android.graphics.drawable.TestStubDrawable
@@ -21,16 +23,23 @@ import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.coroutines.collectLastValue
+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.impl.modes.domain.model.ModesTileModel
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,17 +52,17 @@ import org.mockito.kotlin.verify
@EnableFlags(android.app.Flags.FLAG_MODES_UI)
class ModesTileUserActionInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
private val inputHandler = kosmos.qsTileIntentUserInputHandler
private val mockDialogDelegate = kosmos.mockModesDialogDelegate
+ private val zenModeRepository = kosmos.zenModeRepository
+ private val zenModeInteractor = kosmos.zenModeInteractor
private val underTest =
- ModesTileUserActionInteractor(
- inputHandler,
- mockDialogDelegate,
- )
+ ModesTileUserActionInteractor(inputHandler, mockDialogDelegate, zenModeInteractor)
@Test
- fun handleClick_active() = runTest {
+ fun handleClick_active_showsDialog() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable)
@@ -63,7 +72,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() {
}
@Test
- fun handleClick_inactive() = runTest {
+ fun handleClick_inactive_showsDialog() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable)
@@ -73,7 +82,63 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() {
}
@Test
- fun handleLongClick_active() = runTest {
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_multipleModesActive_deactivatesAll() =
+ testScope.runTest {
+ val activeModes by collectLastValue(zenModeInteractor.activeModes)
+
+ zenModeRepository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder().setName("Mode 1").setActive(true).build(),
+ TestModeBuilder().setName("Mode 2").setActive(true).build(),
+ )
+ )
+ assertThat(activeModes?.modeNames?.count()).isEqualTo(3)
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(
+ data = modelOf(true, listOf("DND", "Mode 1", "Mode 2"))
+ )
+ )
+
+ assertThat(activeModes?.isAnyActive()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_dndActive_deactivatesDnd() =
+ testScope.runTest {
+ val dndMode by collectLastValue(zenModeInteractor.dndMode)
+
+ zenModeRepository.addMode(TestModeBuilder.MANUAL_DND_ACTIVE)
+ assertThat(dndMode?.isActive).isTrue()
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(data = modelOf(true, listOf("DND")))
+ )
+
+ assertThat(dndMode?.isActive).isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_dndInactive_activatesDnd() =
+ testScope.runTest {
+ val dndMode by collectLastValue(zenModeInteractor.dndMode)
+
+ zenModeRepository.addMode(TestModeBuilder.MANUAL_DND_INACTIVE)
+ assertThat(dndMode?.isActive).isFalse()
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(data = modelOf(false, emptyList()))
+ )
+
+ assertThat(dndMode?.isActive).isTrue()
+ }
+
+ @Test
+ fun handleLongClick_active_opensSettings() = runTest {
underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND"))))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
@@ -82,7 +147,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() {
}
@Test
- fun handleLongClick_inactive() = runTest {
+ fun handleLongClick_inactive_opensSettings() = runTest {
underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList())))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
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/viewmodel/QSTileViewModelTest.kt
index 954215eede0d..2edb9c60711b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -173,6 +173,21 @@ class QSTileViewModelTest : SysuiTestCase() {
.isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
}
+ @Test
+ fun tileDetails() =
+ testScope.runTest {
+ assertThat(tileUserActionInteractor.detailsViewModel).isNotNull()
+ assertThat(tileUserActionInteractor.detailsViewModel?.getTitle())
+ .isEqualTo("FakeQSTileUserActionInteractor")
+ assertThat(underTest.detailsViewModel).isNotNull()
+ assertThat(underTest.detailsViewModel?.getTitle())
+ .isEqualTo("FakeQSTileUserActionInteractor")
+
+ tileUserActionInteractor.detailsViewModel = null
+ assertThat(tileUserActionInteractor.detailsViewModel).isNull()
+ assertThat(underTest.detailsViewModel).isNull()
+ }
+
private fun createViewModel(
scope: TestScope,
config: QSTileConfig = tileConfig,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ad6c64b32ddc..039a1dc05c79 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -32,7 +32,6 @@ import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -51,31 +50,22 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
@SmallTest
@RunWith(AndroidJUnit4::class)
class UserSwitchDialogControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var dialogFactory: SystemUIDialog.Factory
- @Mock
- private lateinit var dialog: SystemUIDialog
- @Mock
- private lateinit var falsingManager: FalsingManager
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var userDetailViewAdapter: UserDetailView.Adapter
- @Mock
- private lateinit var launchExpandable: Expandable
- @Mock
- private lateinit var neutralButton: Button
- @Mock
- private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
- @Captor
- private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
+ @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+ @Mock private lateinit var dialog: SystemUIDialog
+ @Mock private lateinit var falsingManager: FalsingManager
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var userDetailViewAdapter: UserDetailView.Adapter
+ @Mock private lateinit var launchExpandable: Expandable
+ @Mock private lateinit var neutralButton: Button
+ @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Captor private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
private lateinit var controller: UserSwitchDialogController
@@ -84,16 +74,17 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(dialog.context).thenReturn(mContext)
- whenever(dialogFactory.create()).thenReturn(dialog)
-
- controller = UserSwitchDialogController(
- { userDetailViewAdapter },
- activityStarter,
- falsingManager,
- mDialogTransitionAnimator,
- uiEventLogger,
- dialogFactory
- )
+ whenever(dialogFactory.create(eq(mContext))).thenReturn(dialog)
+
+ controller =
+ UserSwitchDialogController(
+ { userDetailViewAdapter },
+ activityStarter,
+ falsingManager,
+ mDialogTransitionAnimator,
+ uiEventLogger,
+ dialogFactory,
+ )
}
@Test
@@ -150,7 +141,7 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
.postStartActivityDismissingKeyguard(
argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
eq(0),
- eq(null)
+ eq(null),
)
verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index b5f005cdc706..e56b965d9402 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -47,6 +47,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.currentValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
@@ -77,6 +78,7 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -399,9 +401,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
* Note that this doesn't assert what the current scene is in the UI.
*/
private fun Kosmos.assertCurrentScene(expected: SceneKey) {
- testScope.runCurrent()
assertWithMessage("Current scene mismatch!")
- .that(sceneContainerViewModel.currentScene.value)
+ .that(currentValue(sceneContainerViewModel.currentScene))
.isEqualTo(expected)
}
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 79bb0c401e78..06dd046564df 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
@@ -74,6 +74,7 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.dozeInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -1270,8 +1271,11 @@ class SceneContainerStartableTest : SysuiTestCase() {
authenticationMethod = AuthenticationMethodModel.None,
isLockscreenEnabled = false,
)
- assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ powerInteractor.setAsleepForTest()
underTest.start()
+ runCurrent()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+
powerInteractor.setAwakeForTest()
runCurrent()
@@ -2139,6 +2143,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val transitionStateFlow = prepareState()
underTest.start()
+ runCurrent()
emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer)
assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
@@ -2153,6 +2158,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val transitionStateFlow = prepareState()
underTest.start()
+ runCurrent()
emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer)
assertThat(currentScene).isEqualTo(Scenes.Bouncer)
@@ -2269,6 +2275,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
underTest.start()
+ runCurrent()
sceneInteractor.changeScene(Scenes.Shade, "")
assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
@@ -2350,6 +2357,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
prepareState()
underTest.start()
+ runCurrent()
// run all pending dismiss succeeded/cancelled calls from setup:
kosmos.fakeExecutor.runAllReady()
@@ -2461,13 +2469,18 @@ class SceneContainerStartableTest : SysuiTestCase() {
@Test
fun switchFromDreamToLockscreen_whenLockedAndDreamStopped() =
testScope.runTest {
- keyguardInteractor.setDreaming(true)
val currentScene by collectLastValue(sceneInteractor.currentScene)
prepareState(initialSceneKey = Scenes.Dream)
- assertThat(currentScene).isEqualTo(Scenes.Dream)
underTest.start()
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ runCurrent()
+ keyguardInteractor.setDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
keyguardInteractor.setDreaming(false)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
runCurrent()
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
@@ -2475,13 +2488,18 @@ class SceneContainerStartableTest : SysuiTestCase() {
@Test
fun switchFromDreamToGone_whenUnlockedAndDreamStopped() =
testScope.runTest {
- keyguardInteractor.setDreaming(true)
val currentScene by collectLastValue(sceneInteractor.currentScene)
prepareState(initialSceneKey = Scenes.Dream, isDeviceUnlocked = true)
- assertThat(currentScene).isEqualTo(Scenes.Dream)
underTest.start()
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ runCurrent()
+ keyguardInteractor.setDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
keyguardInteractor.setDreaming(false)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
runCurrent()
assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@@ -2684,6 +2702,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
val currentScene by collectLastValue(sceneInteractor.currentScene)
val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ runCurrent()
sceneInteractor.changeScene(Scenes.Shade, "reason")
sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason")
assertThat(currentScene).isEqualTo(Scenes.Shade)
@@ -2835,8 +2854,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
)
sceneInteractor.setTransitionState(transitionStateFlow)
initialSceneKey?.let {
+ if (isDeviceUnlocked && initialSceneKey != Scenes.Gone) {
+ // Pass through the Gone scene to populate device entry state properly.
+ transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
+ sceneInteractor.changeScene(Scenes.Gone, "prepareState, passing through Gone scene")
+ runCurrent()
+ }
+
transitionStateFlow.value = ObservableTransitionState.Idle(it)
- sceneInteractor.changeScene(it, "reason")
+ sceneInteractor.changeScene(it, "prepareState, initialSceneKey isn't null")
}
if (startsAwake) {
powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 612d646bb7d4..53a083f9ceae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.screenshot
import android.content.Intent
-import androidx.test.ext.junit.runners.AndroidJUnit4
import android.os.Process.myUserHandle
import android.platform.test.annotations.EnableFlags
import android.testing.TestableContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -45,7 +45,7 @@ class ActionIntentExecutorTest : SysuiTestCase() {
private val testableContext = TestableContext(mContext)
private val activityManagerWrapper = mock<ActivityManagerWrapper>()
- private val systemUiProxy = mock<SystemUiProxy>()
+ private val screenshotProxy = mock<ScreenshotProxy>()
private val displayTracker = mock<DisplayTracker>()
@@ -55,7 +55,7 @@ class ActionIntentExecutorTest : SysuiTestCase() {
activityManagerWrapper,
testScope,
mainDispatcher,
- systemUiProxy,
+ screenshotProxy,
displayTracker,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 5d1ce7c5ca05..929537dcf757 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -253,6 +253,16 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
verify(configurationForwarder).onConfigurationChanged(eq(config))
}
+ @Test
+ @EnableFlags(AConfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onMovedToDisplay_configForwarderSet_propagatesConfig() {
+ val config = Configuration()
+
+ underTest.onMovedToDisplay(1, config)
+
+ verify(configurationForwarder).dispatchOnMovedToDisplay(eq(1), eq(config))
+ }
+
private fun captureInteractionEventHandler() {
verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
index 096675962d80..007a0fb87953 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -16,17 +16,20 @@
package com.android.systemui.shade.data.repository
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
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.coroutines.collectValues
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
+import com.android.systemui.shade.display.DefaultDisplayShadePolicy
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,54 +39,72 @@ import org.junit.runner.RunWith
class ShadeDisplaysRepositoryTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val defaultPolicy = SpecificDisplayIdPolicy(0)
-
- private val shadeDisplaysRepository =
- ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope)
+ private val globalSettings = kosmos.fakeGlobalSettings
+ private val displayRepository = kosmos.displayRepository
+ private val defaultPolicy = DefaultDisplayShadePolicy()
+ private val policies = kosmos.shadeDisplayPolicies
+
+ private val underTest =
+ ShadeDisplaysRepositoryImpl(
+ globalSettings,
+ defaultPolicy,
+ testScope.backgroundScope,
+ policies,
+ )
@Test
fun policy_changing_propagatedFromTheLatestPolicy() =
testScope.runTest {
- val displayIds by collectValues(shadeDisplaysRepository.displayId)
- val policy1 = MutablePolicy()
- val policy2 = MutablePolicy()
+ val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).containsExactly(0)
- shadeDisplaysRepository.policy.value = policy1
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")
- policy1.sendDisplayId(1)
+ displayRepository.addDisplay(displayId = 1)
assertThat(displayIds).containsExactly(0, 1)
- policy1.sendDisplayId(2)
+ displayRepository.addDisplay(displayId = 2)
- assertThat(displayIds).containsExactly(0, 1, 2)
+ assertThat(displayIds).containsExactly(0, 1)
- shadeDisplaysRepository.policy.value = policy2
+ displayRepository.removeDisplay(displayId = 1)
- assertThat(displayIds).containsExactly(0, 1, 2, 0)
+ assertThat(displayIds).containsExactly(0, 1, 2)
- policy1.sendDisplayId(4)
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")
- // Changes to the first policy don't affect the output now
assertThat(displayIds).containsExactly(0, 1, 2, 0)
+ }
+
+ @Test
+ fun policy_updatesBasedOnSettingValue_defaultDisplay() =
+ testScope.runTest {
+ val policy by collectLastValue(underTest.policy)
- policy2.sendDisplayId(5)
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")
- assertThat(displayIds).containsExactly(0, 1, 2, 0, 5)
+ assertThat(policy).isInstanceOf(DefaultDisplayShadePolicy::class.java)
}
- private class MutablePolicy : ShadeDisplayPolicy {
- fun sendDisplayId(id: Int) {
- _displayId.value = id
+ @Test
+ fun policy_updatesBasedOnSettingValue_anyExternal() =
+ testScope.runTest {
+ val policy by collectLastValue(underTest.policy)
+
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")
+
+ assertThat(policy).isInstanceOf(AnyExternalShadeDisplayPolicy::class.java)
}
- private val _displayId = MutableStateFlow(0)
- override val name: String
- get() = "mutable_policy"
+ @Test
+ fun policy_updatesBasedOnSettingValue_focusBased() =
+ testScope.runTest {
+ val policy by collectLastValue(underTest.policy)
- override val displayId: StateFlow<Int>
- get() = _displayId
- }
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "status_bar_latest_touch")
+
+ assertThat(policy).isInstanceOf(StatusBarTouchShadeDisplayPolicy::class.java)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
index d584dc9ceef2..eeb3e6b31c69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.shade.ShadePrimaryDisplayCommand
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
@@ -44,18 +45,17 @@ import org.junit.runner.RunWith
class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
+ private val globalSettings = kosmos.fakeGlobalSettings
private val commandRegistry = kosmos.commandRegistry
private val displayRepository = kosmos.displayRepository
private val defaultPolicy = kosmos.defaultShadeDisplayPolicy
- private val policy1 = makePolicy("policy_1")
private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository
+ private val policies = kosmos.shadeDisplayPolicies
private val pw = PrintWriter(StringWriter())
- private val policies =
- setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3"))
-
private val underTest =
ShadePrimaryDisplayCommand(
+ globalSettings,
commandRegistry,
displayRepository,
shadeDisplaysRepository,
@@ -69,30 +69,16 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
}
@Test
- fun commandDisplayOverride_updatesDisplayId() =
- testScope.runTest {
- val displayId by collectLastValue(shadeDisplaysRepository.displayId)
- assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
-
- val newDisplayId = 2
- commandRegistry.onShellCommand(
- pw,
- arrayOf("shade_display_override", newDisplayId.toString()),
- )
-
- assertThat(displayId).isEqualTo(newDisplayId)
- }
-
- @Test
fun commandShadeDisplayOverride_resetsDisplayId() =
testScope.runTest {
val displayId by collectLastValue(shadeDisplaysRepository.displayId)
assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
val newDisplayId = 2
+ displayRepository.addDisplay(displayId = newDisplayId)
commandRegistry.onShellCommand(
pw,
- arrayOf("shade_display_override", newDisplayId.toString()),
+ arrayOf("shade_display_override", "any_external_display"),
)
assertThat(displayId).isEqualTo(newDisplayId)
@@ -108,7 +94,10 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
val newDisplayId = 2
displayRepository.addDisplay(displayId = newDisplayId)
- commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "any_external"))
+ commandRegistry.onShellCommand(
+ pw,
+ arrayOf("shade_display_override", "any_external_display"),
+ )
assertThat(displayId).isEqualTo(newDisplayId)
}
@@ -127,13 +116,14 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
}
@Test
- fun policies_setsSpecificPolicy() =
+ fun policies_setsNewPolicy() =
testScope.runTest {
val policy by collectLastValue(shadeDisplaysRepository.policy)
+ val newPolicy = policies.last().name
- commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name))
+ commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", newPolicy))
- assertThat(policy!!.name).isEqualTo(policy1.name)
+ assertThat(policy!!.name).isEqualTo(newPolicy)
}
private fun makePolicy(policyName: String): ShadeDisplayPolicy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
index ef1ae093bcc9..fd9f5f02ee62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.display
import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
@@ -38,17 +39,31 @@ import org.junit.runner.RunWith
class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
private val displayRepository = kosmos.displayRepository
- val underTest = StatusBarTouchShadeDisplayPolicy(displayRepository, testScope.backgroundScope)
+
+ private fun createUnderTest(
+ shadeOnDefaultDisplayWhenLocked: Boolean = false
+ ): StatusBarTouchShadeDisplayPolicy {
+ return StatusBarTouchShadeDisplayPolicy(
+ displayRepository,
+ keyguardRepository,
+ testScope.backgroundScope,
+ shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
+ )
+ }
@Test
fun displayId_defaultToDefaultDisplay() {
+ val underTest = createUnderTest()
+
assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY)
}
@Test
fun onStatusBarTouched_called_updatesDisplayId() =
testScope.runTest {
+ val underTest = createUnderTest()
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -60,6 +75,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
testScope.runTest {
+ val underTest = createUnderTest()
val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
@@ -72,6 +88,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
testScope.runTest {
+ val underTest = createUnderTest()
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -83,4 +100,40 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
}
+
+ @Test
+ fun onStatusBarTouched_afterKeyguardVisible_goesBackToDefaultDisplay() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ keyguardRepository.setKeyguardShowing(true)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun onStatusBarTouched_afterKeyguardHides_goesBackToPreviousDisplay() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ keyguardRepository.setKeyguardShowing(true)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ keyguardRepository.setKeyguardShowing(false)
+
+ assertThat(displayId).isEqualTo(2)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
index a8d5c31873de..e93d0effe742 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
@@ -16,32 +16,26 @@
package com.android.systemui.shade.domain.interactor
-import android.content.mockedContext
import android.content.res.Configuration
import android.content.res.mockResources
import android.view.Display
-import android.view.mockWindowManager
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.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.ui.view.mockShadeRootView
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.testKosmos
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.advanceUntilIdle
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class ShadeDisplaysInteractorTest : SysuiTestCase() {
@@ -49,9 +43,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
private val shadeRootview = kosmos.mockShadeRootView
private val positionRepository = kosmos.fakeShadeDisplaysRepository
- private val shadeContext = kosmos.mockedContext
- private val testScope = kosmos.testScope
- private val shadeWm = kosmos.mockWindowManager
+ private val shadeContext = kosmos.mockedWindowContext
private val resources = kosmos.mockResources
private val configuration = mock<Configuration>()
private val display = mock<Display>()
@@ -66,8 +58,8 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
whenever(resources.configuration).thenReturn(configuration)
whenever(shadeContext.displayId).thenReturn(0)
- whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm)
whenever(shadeContext.resources).thenReturn(resources)
+ whenever(shadeContext.display).thenReturn(display)
}
@Test
@@ -77,7 +69,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
underTest.start()
- verifyNoMoreInteractions(shadeWm)
+ verify(shadeContext, never()).reparentToDisplay(any())
}
@Test
@@ -87,24 +79,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
underTest.start()
- inOrder(shadeWm).apply {
- verify(shadeWm).removeView(eq(shadeRootview))
- verify(shadeWm).addView(eq(shadeRootview), any())
- }
- }
-
- @Test
- fun start_shadePositionChanges_removedThenAdded() {
- whenever(display.displayId).thenReturn(0)
- positionRepository.setDisplayId(0)
- underTest.start()
-
- positionRepository.setDisplayId(1)
- testScope.advanceUntilIdle()
-
- inOrder(shadeWm).apply {
- verify(shadeWm).removeView(eq(shadeRootview))
- verify(shadeWm).addView(eq(shadeRootview), any())
- }
+ verify(shadeContext).reparentToDisplay(eq(1))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
index 7842d75db6c4..f9b44886ec3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -111,6 +111,8 @@ class CommunalSmartspaceControllerTest : SysuiTestCase() {
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {}
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index c83c82dc0914..70ba00e82241 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -127,6 +127,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {}
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 5b0b59de47c2..4a3be4487290 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -31,6 +31,10 @@ import static android.os.UserHandle.USER_ALL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -280,8 +284,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
mBackgroundExecutor.runAllReady();
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
}
@Test
@@ -357,7 +363,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN current user's notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
}
@Test
@@ -368,7 +375,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN current user's notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
}
@Test
@@ -385,7 +393,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
.setChannel(channel)
.setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
// THEN the notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
}
@Test
@@ -399,7 +408,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
.setChannel(null)
.setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
// THEN the notification is not redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
}
@Test
@@ -410,7 +420,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN work profile notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
}
@@ -422,7 +433,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN work profile notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
}
@@ -440,11 +452,14 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN the work profile notification doesn't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
// THEN the current user and secondary user notifications do need to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
}
@Test
@@ -461,11 +476,14 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN the work profile notification needs to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
// THEN the current user and secondary user notifications don't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_NONE,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
}
@Test
@@ -481,18 +499,20 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
// THEN the secondary profile notification still needs to be redacted because the current
// user's setting takes precedence
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
}
@Test
public void testHasSensitiveContent_redacted() {
// Allow private notifications for this user
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mCurrentUser.id);
changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// Sensitive Content notifications are always redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mSensitiveContentNotif));
+ assertEquals(REDACTION_TYPE_SENSITIVE_CONTENT,
+ mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
}
@Test
@@ -707,9 +727,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
.putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
// it's a global field, confirm secondary too
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
mSecondaryUser.id));
@@ -732,7 +754,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
}
@@ -763,7 +786,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
- assertTrue(mLockscreenUserManager.needsRedaction(notifEntry));
+ assertEquals(REDACTION_TYPE_PUBLIC, mLockscreenUserManager.getRedactionType(notifEntry));
}
@Test
@@ -784,7 +807,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertEquals(REDACTION_TYPE_PUBLIC,
+ mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
}
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 3ebf9f72b5ff..a62d9d5ce62f 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
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.StatusBarIconView
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.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -163,6 +164,28 @@ class CallChipViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ val notifKey = "testNotifKey"
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 1000, notificationIcon = null, notificationKey = notifKey)
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isInstanceOf(
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon::class.java
+ )
+ val actualNotifKey =
+ (((latest as OngoingActivityChipModel.Shown).icon)
+ as OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon)
+ .notificationKey
+ assertThat(actualNotifKey).isEqualTo(notifKey)
+ }
+
+ @Test
@DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
fun chip_zeroStartTime_notifIconFlagOff_iconIsPhone() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index 611318acde96..dea3d1f68ce5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -197,7 +197,7 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() {
if (
(it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE
) {
- emptyList()
+ emptyList<ResolveInfo>()
} else {
listOf(mock<ResolveInfo>())
}
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 e96dd16e9023..9ad1f409a8ea 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
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -25,7 +26,9 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
+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.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -43,7 +46,11 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
kosmos.runTest {
val icon = mock<StatusBarIconView>()
val startingNotif =
- activeNotificationModel(key = "notif1", statusBarChipIcon = icon, whenTime = 5432)
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = icon,
+ promotedContent = PROMOTED_CONTENT,
+ )
val underTest = factory.create(startingNotif)
@@ -51,7 +58,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
assertThat(latest!!.key).isEqualTo("notif1")
assertThat(latest!!.statusBarChipIconView).isEqualTo(icon)
- assertThat(latest!!.whenTime).isEqualTo(5432)
+ assertThat(latest!!.promotedContent).isEqualTo(PROMOTED_CONTENT)
}
@Test
@@ -60,7 +67,11 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
val originalIconView = mock<StatusBarIconView>()
val underTest =
factory.create(
- activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView)
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = originalIconView,
+ promotedContent = PROMOTED_CONTENT,
+ )
)
val latest by collectLastValue(underTest.notificationChip)
@@ -70,13 +81,12 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = newIconView,
- whenTime = 6543,
+ promotedContent = PROMOTED_CONTENT,
)
)
assertThat(latest!!.key).isEqualTo("notif1")
assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView)
- assertThat(latest!!.whenTime).isEqualTo(6543)
}
@Test
@@ -85,14 +95,22 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
val originalIconView = mock<StatusBarIconView>()
val underTest =
factory.create(
- activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView)
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = originalIconView,
+ promotedContent = PROMOTED_CONTENT,
+ )
)
val latest by collectLastValue(underTest.notificationChip)
val newIconView = mock<StatusBarIconView>()
underTest.setNotification(
- activeNotificationModel(key = "other_notif", statusBarChipIcon = newIconView)
+ activeNotificationModel(
+ key = "other_notif",
+ statusBarChipIcon = newIconView,
+ promotedContent = PROMOTED_CONTENT,
+ )
)
assertThat(latest!!.key).isEqualTo("notif1")
@@ -100,10 +118,43 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
}
@Test
+ fun notificationChip_ignoresSetWithNullPromotedContent() =
+ kosmos.runTest {
+ val originalIconView = mock<StatusBarIconView>()
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = originalIconView,
+ promotedContent = PROMOTED_CONTENT,
+ )
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ val newIconView = mock<StatusBarIconView>()
+ underTest.setNotification(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = newIconView,
+ promotedContent = null,
+ )
+ )
+
+ assertThat(latest!!.statusBarChipIconView).isEqualTo(originalIconView)
+ }
+
+ @Test
fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() =
kosmos.runTest {
val underTest =
- factory.create(activeNotificationModel(key = "notif1", statusBarChipIcon = null))
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ )
+ )
val latest by collectLastValue(underTest.notificationChip)
@@ -111,17 +162,88 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_cdEnabled_missingStatusBarIconChipView_inConstructor_emitsNotNull() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ )
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.key).isEqualTo("notif1")
+ }
+
+ @Test
fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() =
kosmos.runTest {
- val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = mock())
+ val startingNotif =
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
+ val underTest = factory.create(startingNotif)
+ val latest by collectLastValue(underTest.notificationChip)
+ assertThat(latest).isNotNull()
+
+ underTest.setNotification(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ )
+ )
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_missingStatusBarIconChipView_inSet_cdEnabled_emitsNotNull() =
+ kosmos.runTest {
+ val startingNotif =
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
val underTest = factory.create(startingNotif)
val latest by collectLastValue(underTest.notificationChip)
assertThat(latest).isNotNull()
underTest.setNotification(
- activeNotificationModel(key = "notif1", statusBarChipIcon = null)
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ )
)
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.key).isEqualTo("notif1")
+ }
+
+ @Test
+ fun notificationChip_missingPromotedContent_inConstructor_emitsNull() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = mock(),
+ promotedContent = null,
+ )
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
assertThat(latest).isNull()
}
@@ -132,7 +254,12 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
val underTest =
factory.create(
- activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ activeNotificationModel(
+ key = "notif",
+ uid = UID,
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
)
val latest by collectLastValue(underTest.notificationChip)
@@ -147,7 +274,12 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
val underTest =
factory.create(
- activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ activeNotificationModel(
+ key = "notif",
+ uid = UID,
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
)
val latest by collectLastValue(underTest.notificationChip)
@@ -160,7 +292,12 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
kosmos.runTest {
val underTest =
factory.create(
- activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ activeNotificationModel(
+ key = "notif",
+ uid = UID,
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
)
val latest by collectLastValue(underTest.notificationChip)
@@ -192,6 +329,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
key = "notif",
uid = hiddenUid,
statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
)
)
val latest by collectLastValue(underTest.notificationChip)
@@ -200,7 +338,12 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
// WHEN the notif gets a new UID that starts as visible
activityManagerRepository.fake.startingIsAppVisibleValue = true
underTest.setNotification(
- activeNotificationModel(key = "notif", uid = shownUid, statusBarChipIcon = mock())
+ activeNotificationModel(
+ key = "notif",
+ uid = shownUid,
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
)
// THEN we re-fetch the app visibility state with the new UID, and since that UID is
@@ -210,5 +353,6 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
companion object {
private const val UID = 885
+ private val PROMOTED_CONTENT = PromotedNotificationContentModel.Builder("notif1").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 11831ca97389..165e943a0cc0 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,21 +22,30 @@ 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.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
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.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+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.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -46,7 +55,12 @@ import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
class NotifChipsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val kosmos =
+ testKosmos().useUnconfinedTestDispatcher().apply {
+ // Don't be in lockscreen so that HUNs are allowed
+ fakeKeyguardTransitionRepository =
+ FakeKeyguardTransitionRepository(initInLockscreen = false, testScope = testScope)
+ }
private val activeNotificationListRepository = kosmos.activeNotificationListRepository
private val underTest by lazy { kosmos.notifChipsViewModel }
@@ -102,11 +116,65 @@ class NotifChipsViewModelTest : SysuiTestCase() {
assertThat(latest).hasSize(1)
val chip = latest!![0]
- assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_onePromotedNotif_connectedDisplaysFlagEnabled_statusBarIconMatches() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val notifKey = "notif"
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = notifKey,
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder(notifKey).build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ val chip = latest!![0]
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(chip.icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notifKey))
+ }
+
+ @Test
+ fun chips_onePromotedNotif_colorMatches() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.colors =
+ PromotedNotificationContentModel.Colors(
+ backgroundColor = 56,
+ primaryTextColor = 89,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ val colors = latest!![0].colors
+ assertThat(colors).isInstanceOf(ColorsModel.Custom::class.java)
+ assertThat((colors as ColorsModel.Custom).backgroundColorInt).isEqualTo(56)
+ assertThat((colors as ColorsModel.Custom).primaryTextColorInt).isEqualTo(89)
+ }
+
+ @Test
fun chips_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -139,6 +207,241 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_connectedDisplaysFlagEnabled_onlyForPromotedNotifs() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val firstKey = "notif1"
+ val secondKey = "notif2"
+ val thirdKey = "notif3"
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = firstKey,
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder(firstKey).build(),
+ ),
+ activeNotificationModel(
+ key = secondKey,
+ statusBarChipIcon = null,
+ promotedContent =
+ PromotedNotificationContentModel.Builder(secondKey).build(),
+ ),
+ activeNotificationModel(
+ key = thirdKey,
+ statusBarChipIcon = null,
+ promotedContent = null,
+ ),
+ )
+ )
+
+ assertThat(latest).hasSize(2)
+ assertIsNotifKey(latest!![0], firstKey)
+ assertIsNotifKey(latest!![1], secondKey)
+ }
+
+ @Test
+ fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.shortCriticalText = "Arrived"
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
+ assertThat((latest!![0] as OngoingActivityChipModel.Shown.Text).text)
+ .isEqualTo("Arrived")
+ }
+
+ @Test
+ fun chips_noTime_isIconOnly() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply { this.time = null }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
+ fun chips_basicTime_isShortTimeDelta() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ }
+
+ @Test
+ fun chips_countUpTime_isTimer() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.CountUp,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+ }
+
+ @Test
+ fun chips_countDownTime_isTimer() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.CountDown,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+ }
+
+ @Test
+ fun chips_noHeadsUp_showsTime() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ // WHEN there's no HUN
+ kosmos.headsUpNotificationRepository.setNotifications(emptyList())
+
+ // THEN the chip shows the time
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ }
+
+ @Test
+ fun chips_hasHeadsUpByUser_onlyShowsIcon() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ // WHEN there's a HUN pinned by a user
+ kosmos.headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "notif",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
fun chips_clickingChipNotifiesInteractor() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -173,10 +476,13 @@ class NotifChipsViewModelTest : SysuiTestCase() {
companion object {
fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
- assertThat(latest)
- .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
}
+
+ fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) {
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(expectedKey))
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
index 1b3f29a6f0c5..dd1b36925363 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.core
+import android.internal.statusbar.FakeStatusBarService.Companion.SECONDARY_DISPLAY_ID
import android.internal.statusbar.fakeStatusBarService
import android.platform.test.annotations.EnableFlags
import android.view.WindowInsets
@@ -53,7 +54,7 @@ class CommandQueueInitializerTest : SysuiTestCase() {
}
@Test
- fun start_barResultHasTransientStatusBar_transientStateIsTrue() {
+ fun start_defaultDisplay_barResultHasTransientStatusBar_transientStateIsTrue() {
fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
initializer.start()
@@ -62,7 +63,7 @@ class CommandQueueInitializerTest : SysuiTestCase() {
}
@Test
- fun start_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+ fun start_defaultDisplay_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
initializer.start()
@@ -71,6 +72,32 @@ class CommandQueueInitializerTest : SysuiTestCase() {
}
@Test
+ fun start_secondaryDisplay_barResultHasTransientStatusBar_transientStateIsTrue() {
+ fakeStatusBarService.transientBarTypesSecondaryDisplay = WindowInsets.Type.statusBars()
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.forDisplay(SECONDARY_DISPLAY_ID).isTransientShown.value)
+ .isTrue()
+ // Default display should be unaffected
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun start_secondaryDisplay_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+ fakeStatusBarService.transientBarTypesSecondaryDisplay = WindowInsets.Type.navigationBars()
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.forDisplay(SECONDARY_DISPLAY_ID).isTransientShown.value)
+ .isFalse()
+ // Default display should be unaffected
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isTrue()
+ }
+
+ @Test
fun start_callsOnSystemBarAttributesChanged_basedOnRegisterBarResult() {
initializer.start()
@@ -85,6 +112,17 @@ class CommandQueueInitializerTest : SysuiTestCase() {
fakeStatusBarService.packageName,
fakeStatusBarService.letterboxDetails,
)
+ verify(commandQueueCallbacks)
+ .onSystemBarAttributesChanged(
+ SECONDARY_DISPLAY_ID,
+ fakeStatusBarService.appearanceSecondaryDisplay,
+ fakeStatusBarService.appearanceRegionsSecondaryDisplay,
+ fakeStatusBarService.navbarColorManagedByImeSecondaryDisplay,
+ fakeStatusBarService.behaviorSecondaryDisplay,
+ fakeStatusBarService.requestedVisibleTypesSecondaryDisplay,
+ fakeStatusBarService.packageNameSecondaryDisplay,
+ fakeStatusBarService.letterboxDetailsSecondaryDisplay,
+ )
}
@Test
@@ -105,6 +143,14 @@ class CommandQueueInitializerTest : SysuiTestCase() {
fakeStatusBarService.imeBackDisposition,
fakeStatusBarService.showImeSwitcher,
)
+
+ verify(commandQueueCallbacks)
+ .setImeWindowStatus(
+ SECONDARY_DISPLAY_ID,
+ fakeStatusBarService.imeWindowVisSecondaryDisplay,
+ fakeStatusBarService.imeBackDispositionSecondaryDisplay,
+ fakeStatusBarService.showImeSwitcherSecondaryDisplay,
+ )
}
@Test
@@ -117,6 +163,11 @@ class CommandQueueInitializerTest : SysuiTestCase() {
.isEqualTo(fakeStatusBarService.disabledFlags1)
assertThat(commandQueue.disableFlags2ForDisplay(context.displayId))
.isEqualTo(fakeStatusBarService.disabledFlags2)
+
+ assertThat(commandQueue.disableFlags1ForDisplay(SECONDARY_DISPLAY_ID))
+ .isEqualTo(fakeStatusBarService.disabledFlags1SecondaryDisplay)
+ assertThat(commandQueue.disableFlags2ForDisplay(SECONDARY_DISPLAY_ID))
+ .isEqualTo(fakeStatusBarService.disabledFlags2SecondaryDisplay)
}
@Test
@@ -125,5 +176,7 @@ class CommandQueueInitializerTest : SysuiTestCase() {
assertThat(commandQueue.disableFlags1ForDisplay(context.displayId)).isNull()
assertThat(commandQueue.disableFlags2ForDisplay(context.displayId)).isNull()
+ assertThat(commandQueue.disableFlags1ForDisplay(SECONDARY_DISPLAY_ID)).isNull()
+ assertThat(commandQueue.disableFlags2ForDisplay(SECONDARY_DISPLAY_ID)).isNull()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index fe287ef98729..611ae3dbefcf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -1109,6 +1109,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {
+ }
})
}
}
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 1d7f25784327..a3ffd91b1728 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
@@ -50,7 +50,6 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
@@ -76,6 +75,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -315,7 +315,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// WHEN the notification is added but not yet binding
collectionListener.onEntryAdded(entry)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
// THEN only promote mEntry
assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
@@ -330,7 +330,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(entry)
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
- verify(headsUpViewBinder).bindHeadsUpView(eq(entry), any())
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), eq(false), any())
// THEN only promote mEntry
assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
@@ -382,11 +382,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(entry)
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
- verify(headsUpManager, never()).showNotification(entry)
- withArgCaptor<BindCallback> {
- verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
- }
- .onBindFinished(entry)
+
+ finishBind(entry)
// THEN we tell the HeadsUpManager to show the notification
verify(headsUpManager).showNotification(entry)
@@ -401,8 +398,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN we never bind the heads up view or tell HeadsUpManager to show the notification
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
- verify(headsUpManager, never()).showNotification(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+ verify(headsUpManager, never()).showNotification(eq(entry), any())
}
@Test
@@ -435,8 +432,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
}
@Test
@@ -471,7 +468,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
finishBind(entry)
- verify(headsUpManager).showNotification(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = true)
}
@Test
@@ -485,8 +482,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
executor.runAllReady()
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
- verify(headsUpManager, never()).showNotification(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+ verify(headsUpManager, never()).showNotification(eq(entry), any())
}
@Test
@@ -509,7 +506,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN it's still shown as heads up
finishBind(entry)
- verify(headsUpManager).showNotification(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = true)
}
@Test
@@ -525,8 +522,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(promotedEntry))
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(promotedEntry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(promotedEntry), any())
- verify(headsUpManager, never()).showNotification(promotedEntry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(promotedEntry), any(), any())
+ verify(headsUpManager, never()).showNotification(eq(promotedEntry), any())
// Then a new notification comes in that should be heads up
setShouldHeadsUp(entry, false)
@@ -544,10 +541,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN the promoted entry is shown as a HUN, *not* the new entry
finishBind(promotedEntry)
- verify(headsUpManager).showNotification(promotedEntry)
+ verify(headsUpManager).showNotification(promotedEntry, isPinnedByUser = true)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
- verify(headsUpManager, never()).showNotification(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+ verify(headsUpManager, never()).showNotification(eq(entry), any())
}
@Test
@@ -558,10 +555,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(groupSummary)
collectionListener.onEntryAdded(groupSibling1)
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupSibling1))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupSibling1))
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupSibling1)
verify(headsUpManager, never()).showNotification(groupSummary)
@@ -580,10 +577,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(groupSummary)
collectionListener.onEntryAdded(groupChild1)
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupChild1))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupChild1))
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupChild1)
verify(headsUpManager, never()).showNotification(groupSummary)
@@ -603,12 +600,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(groupSibling2)
val entryList = listOf(groupSibling1, groupSibling2)
beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupSibling1)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
// THEN we tell the HeadsUpManager to show the notification
verify(headsUpManager, never()).showNotification(groupSummary)
@@ -629,12 +626,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
collectionListener.onEntryAdded(groupChild2)
val entryList = listOf(groupChild1, groupChild2)
beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupChild1)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
// THEN we tell the HeadsUpManager to show the notification
verify(headsUpManager, never()).showNotification(groupSummary)
@@ -661,7 +658,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
val afterTransformGroup =
GroupEntryBuilder()
@@ -672,10 +669,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
listOf(groupPriority, afterTransformGroup)
)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupPriority)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
// THEN we tell the HeadsUpManager to show the notification
verify(headsUpManager, never()).showNotification(groupSummary)
@@ -702,7 +699,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
val afterTransformGroup =
GroupEntryBuilder()
@@ -713,10 +710,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
listOf(groupPriority, afterTransformGroup)
)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupPriority)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
verify(headsUpManager, never()).showNotification(groupSummary)
verify(headsUpManager).showNotification(groupPriority)
@@ -740,7 +737,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
val afterTransformGroup =
GroupEntryBuilder()
@@ -751,10 +748,10 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
listOf(groupPriority, afterTransformGroup)
)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupPriority)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
verify(headsUpManager, never()).showNotification(groupSummary)
verify(headsUpManager).showNotification(groupPriority)
@@ -779,7 +776,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
val afterTransformGroup =
GroupEntryBuilder()
@@ -791,9 +788,9 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
)
finishBind(groupSummary)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
verify(headsUpManager).showNotification(groupSummary)
verify(headsUpManager, never()).showNotification(groupPriority)
@@ -816,12 +813,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupSibling1, groupSibling2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
finishBind(groupSummary)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
verify(headsUpManager).showNotification(groupSummary)
verify(headsUpManager, never()).showNotification(groupSibling1)
@@ -842,12 +839,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupChild1, groupChild2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
finishBind(groupSummary)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild1), any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild1), any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
verify(headsUpManager).showNotification(groupSummary)
verify(headsUpManager, never()).showNotification(groupChild1)
@@ -871,12 +868,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
.setChildren(listOf(groupChild1, groupChild2))
.build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
finishBind(groupChild1)
- verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
verify(headsUpManager, never()).showNotification(groupSummary)
verify(headsUpManager).showNotification(groupChild1)
@@ -900,7 +897,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN the notification is shown
finishBind(entry)
- verify(headsUpManager).showNotification(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
}
@Test
@@ -917,8 +914,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
}
@Test
@@ -938,7 +935,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN the notification is shown
finishBind(entry)
- verify(headsUpManager).showNotification(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
}
@Test
@@ -958,8 +955,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
}
@Test
@@ -1022,8 +1019,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN it should full screen and log but it should NOT HUN
verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
verifyLoggedFullScreenIntentDecision(
entry,
FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
@@ -1063,8 +1060,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN it should still not yet full screen or HUN
verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
// Same decision as before; is not logged
verifyNoFullScreenIntentDecisionLogged()
@@ -1080,8 +1077,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN it should full screen and log but it should NOT HUN
verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
verifyLoggedFullScreenIntentDecision(
entry,
FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
@@ -1107,8 +1104,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// THEN it should NOT full screen or HUN
verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(headsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+ verify(headsUpManager, never()).showNotification(any(), any())
// NOW the DND logic changes and FSI and HUN are available
clearInvocations(launchFullScreenIntentProvider)
@@ -1121,7 +1118,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// VERIFY that the FSI didn't happen, but that we do HUN
verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
finishBind(entry)
- verify(headsUpManager).showNotification(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
}
@Test
@@ -1206,10 +1203,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
}
private fun finishBind(entry: NotificationEntry) {
- verify(headsUpManager, never()).showNotification(entry)
- withArgCaptor<BindCallback> {
- verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ verify(headsUpManager, never()).showNotification(eq(entry), any())
+ val isPinnedByUserCaptor = argumentCaptor<Boolean>()
+ withArgCaptor<HeadsUpViewBinder.HeadsUpBindCallback> {
+ verify(headsUpViewBinder)
+ .bindHeadsUpView(eq(entry), isPinnedByUserCaptor.capture(), capture())
}
- .onBindFinished(entry)
+ .onHeadsUpBindFinished(entry, isPinnedByUserCaptor.firstValue)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 689fc7cb647b..8f21ddff524c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
@@ -22,671 +24,811 @@ import android.app.NotificationManager
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.service.notification.StatusBarNotification
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.fakeStatusBarStateController
+import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notification.mockDynamicPrivacyController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
+import com.android.systemui.statusbar.policy.mockSensitiveNotificationProtectionController
+import com.android.systemui.statusbar.policy.sensitiveNotificationProtectionController
import com.android.systemui.testKosmos
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-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.withArgCaptor
-import dagger.BindsInstance
-import dagger.Component
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SensitiveContentCoordinatorTest : SysuiTestCase() {
-
- val kosmos = testKosmos()
-
- val dynamicPrivacyController: DynamicPrivacyController = mock()
- val lockscreenUserManager: NotificationLockscreenUserManager = mock()
- val pipeline: NotifPipeline = mock()
- val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
- val statusBarStateController: StatusBarStateController = mock()
- val keyguardStateController: KeyguardStateController = mock()
- val mSelectedUserInteractor: SelectedUserInteractor = mock()
+@RunWith(ParameterizedAndroidJunit4::class)
+class SensitiveContentCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ val kosmos =
+ testKosmos().apply {
+ // Override some Kosmos objects with mocks or fakes for easier testability
+ dynamicPrivacyController = mockDynamicPrivacyController
+ sensitiveNotificationProtectionController =
+ mockSensitiveNotificationProtectionController
+ statusBarStateController = fakeStatusBarStateController
+ }
+
+ val testScope = kosmos.testScope
+
+ val dynamicPrivacyController: DynamicPrivacyController = kosmos.mockDynamicPrivacyController
+ val lockscreenUserManager: NotificationLockscreenUserManager =
+ kosmos.notificationLockscreenUserManager
+ val pipeline: NotifPipeline = kosmos.notifPipeline
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
+ val statusBarStateController: SysuiStatusBarStateController =
+ kosmos.fakeStatusBarStateController
val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController =
- mock()
- val deviceEntryInteractor: DeviceEntryInteractor = mock()
- val sceneInteractor: SceneInteractor = mock()
-
- val coordinator: SensitiveContentCoordinator =
- DaggerTestSensitiveContentCoordinatorComponent.factory()
- .create(
- dynamicPrivacyController,
- lockscreenUserManager,
- keyguardUpdateMonitor,
- statusBarStateController,
- keyguardStateController,
- mSelectedUserInteractor,
- sensitiveNotificationProtectionController,
- deviceEntryInteractor,
- sceneInteractor,
- kosmos.applicationCoroutineScope,
- )
- .coordinator
+ kosmos.mockSensitiveNotificationProtectionController
+ val deviceEntryInteractor: DeviceEntryInteractor
+ get() = kosmos.deviceEntryInteractor
- @Test
- fun onDynamicPrivacyChanged_invokeInvalidationListener() {
- coordinator.attach(pipeline)
- val invalidator =
- withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
- val dynamicPrivacyListener =
- withArgCaptor<DynamicPrivacyController.Listener> {
- verify(dynamicPrivacyController).addListener(capture())
- }
+ val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
+ val sceneInteractor: SceneInteractor
+ get() = kosmos.sceneInteractor
- val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
- invalidator.setInvalidationListener(invalidationListener)
+ val coordinator: SensitiveContentCoordinator by lazy { kosmos.sensitiveContentCoordinator }
- dynamicPrivacyListener.onDynamicPrivacyChanged()
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
- verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Test
- @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onSensitiveStateChanged_invokeInvalidationListener() {
- coordinator.attach(pipeline)
- val invalidator =
- withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
- val onSensitiveStateChangedListener =
- withArgCaptor<Runnable> {
- verify(sensitiveNotificationProtectionController)
- .registerSensitiveStateListener(capture())
- }
-
- val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
- invalidator.setInvalidationListener(invalidationListener)
+ @EnableSceneContainer
+ fun onLockscreenDynamicallyUnlocked_invokeInvalidationListener() =
+ testScope.runTest {
+ // Setup
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
+
+ // Given the lockscreen is shown
+ setupLockScreen(canSwipeUp = false)
+ clearInvocations(invalidationListener)
+
+ // When the device gets unlocked
+ faceAuthRepository.isAuthenticated.value = true
+ runCurrent()
+
+ // Then the invalidationListener is called
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
- onSensitiveStateChangedListener.run()
+ @Test
+ @DisableSceneContainer
+ fun onDynamicPrivacyChanged_invokeInvalidationListener() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val dynamicPrivacyListener =
+ withArgCaptor<DynamicPrivacyController.Listener> {
+ verify(dynamicPrivacyController).addListener(capture())
+ }
+
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
+
+ dynamicPrivacyListener.onDynamicPrivacyChanged()
+
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
- verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
- }
+ @Test
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ fun onSensitiveStateChanged_invokeInvalidationListener() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val onSensitiveStateChangedListener =
+ withArgCaptor<Runnable> {
+ verify(sensitiveNotificationProtectionController)
+ .registerSensitiveStateListener(capture())
+ }
+
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
+
+ onSensitiveStateChangedListener.run()
+
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
@Test
@DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_flagDisabled_filterNoAdded() {
- coordinator.attach(pipeline)
+ fun screenshareSecretFilter_flagDisabled_filterNoAdded() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
- verify(pipeline, never()).addFinalizeFilter(any(NotifFilter::class.java))
- }
+ verify(pipeline, never()).addFinalizeFilter(any())
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false)
-
- coordinator.attach(pipeline)
- val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
-
- val defaultNotification = createNotificationEntry("test", false, false)
- val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
- val notificationOnSecretChannel = createNotificationEntry("test", false, true)
-
- assertFalse(filter.shouldFilterOut(defaultNotification, 0))
- assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
- assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0))
- }
+ fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() =
+ testScope.runTest {
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(false)
+
+ coordinator.attach(pipeline)
+ val filter =
+ withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
+
+ val defaultNotification = createNotificationEntry("test", false, false)
+ val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
+ val notificationOnSecretChannel = createNotificationEntry("test", false, true)
+
+ assertFalse(filter.shouldFilterOut(defaultNotification, 0))
+ assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
+ assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0))
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_sensitiveActive_filtersSecret() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- coordinator.attach(pipeline)
- val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
-
- val defaultNotification = createNotificationEntry("test", false, false)
- val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
- val notificationOnSecretChannel = createNotificationEntry("test", false, true)
-
- assertFalse(filter.shouldFilterOut(defaultNotification, 0))
- assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
- assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0))
- }
+ fun screenshareSecretFilter_sensitiveActive_filtersSecret() =
+ testScope.runTest {
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ coordinator.attach(pipeline)
+ val filter =
+ withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
+
+ val defaultNotification = createNotificationEntry("test", false, false)
+ val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
+ val notificationOnSecretChannel = createNotificationEntry("test", false, true)
+
+ assertFalse(filter.shouldFilterOut(defaultNotification, 0))
+ assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
+ assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0))
+ }
@Test
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- }
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(true, true)
- }
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(true, true)
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
+ .thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+ whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any()))
+ .thenReturn(true)
+ statusBarStateController.state = StatusBarState.KEYGUARD
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
+ verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any())
+ }
+
+ private fun TestScope.setupLockScreen(canSwipeUp: Boolean) {
+ if (SceneContainerFlag.isEnabled) {
+ val authMethod =
+ if (canSwipeUp) AuthenticationMethodModel.None
+ else AuthenticationMethodModel.Password
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+ kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+ switchToScene(Scenes.Lockscreen)
+ } else {
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(canSwipeUp)
+ }
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD)
- whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
- .thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
- whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any()))
- .thenReturn(true)
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
- verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any())
+ private fun TestScope.switchToScene(sceneKey: SceneKey) {
+ sceneInteractor.changeScene(sceneKey, "reason")
+ sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(sceneKey)))
+ runCurrent()
}
private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
@@ -700,7 +842,13 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
whenever(sbn).thenReturn(mockSbn)
whenever(row).thenReturn(mockRow)
}
- whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
+ val redactionType =
+ if (needsRedaction) {
+ REDACTION_TYPE_PUBLIC
+ } else {
+ REDACTION_TYPE_NONE
+ }
+ whenever(lockscreenUserManager.getRedactionType(mockEntry)).thenReturn(redactionType)
whenever(mockEntry.rowExists()).thenReturn(true)
return object : ListEntry("key", 0) {
override fun getRepresentativeEntry(): NotificationEntry = mockEntry
@@ -733,26 +881,3 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
return notificationEntry
}
}
-
-@CoordinatorScope
-@Component(modules = [SensitiveContentCoordinatorModule::class])
-interface TestSensitiveContentCoordinatorComponent {
- val coordinator: SensitiveContentCoordinator
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance dynamicPrivacyController: DynamicPrivacyController,
- @BindsInstance lockscreenUserManager: NotificationLockscreenUserManager,
- @BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor,
- @BindsInstance statusBarStateController: StatusBarStateController,
- @BindsInstance keyguardStateController: KeyguardStateController,
- @BindsInstance selectedUserInteractor: SelectedUserInteractor,
- @BindsInstance
- sensitiveNotificationProtectionController: SensitiveNotificationProtectionController,
- @BindsInstance deviceEntryInteractor: DeviceEntryInteractor,
- @BindsInstance sceneInteractor: SceneInteractor,
- @BindsInstance @Application scope: CoroutineScope,
- ): TestSensitiveContentCoordinatorComponent
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 2f77b33c96cb..3c772fdbe0b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -27,6 +27,8 @@ import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDIN
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -42,6 +44,7 @@ import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Before
@@ -212,12 +215,12 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
.thenReturn(false)
val oldAdjustment: NotifUiAdjustment = adjustmentProvider.calculateAdjustment(entry)
- assertFalse(oldAdjustment.needsRedaction)
+ assertEquals(REDACTION_TYPE_NONE, oldAdjustment.redactionType)
whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
.thenReturn(true)
val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
- assertTrue(newAdjustment.needsRedaction)
+ assertEquals(REDACTION_TYPE_PUBLIC, newAdjustment.redactionType)
// Then: need re-inflation
assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
@@ -229,12 +232,12 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
.thenReturn(false)
val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
- assertFalse(oldAdjustment.needsRedaction)
+ assertEquals(REDACTION_TYPE_NONE, oldAdjustment.redactionType)
whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
.thenReturn(true)
val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
- assertFalse(newAdjustment.needsRedaction)
+ assertEquals(REDACTION_TYPE_NONE, newAdjustment.redactionType)
// Then: need no re-inflation
assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index dc0231f40609..22ef408e266c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
@@ -409,76 +410,101 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() {
}
@Test
- fun showHeadsUpStatusBar_true() =
+ fun statusBarHeadsUpState_pinnedBySystem() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
- // WHEN a row is pinned
- headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+ headsUpRepository.setNotifications(
+ FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedBySystem)
+ )
+ runCurrent()
- assertThat(showHeadsUpStatusBar).isTrue()
+ assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedBySystem)
}
@Test
- fun showHeadsUpStatusBar_withoutPinnedNotifications_false() =
+ fun statusBarHeadsUpState_pinnedByUser() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
- // WHEN no row is pinned
- headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = false))
+ headsUpRepository.setNotifications(
+ FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedByUser)
+ )
+ runCurrent()
- assertThat(showHeadsUpStatusBar).isFalse()
+ assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedByUser)
}
@Test
- fun showHeadsUpStatusBar_whenShadeExpanded_false() =
+ fun statusBarHeadsUpState_withoutPinnedNotifications_notPinned() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+
+ headsUpRepository.setNotifications(
+ FakeHeadsUpRowRepository(key = "key 0", PinnedStatus.NotPinned)
+ )
+ runCurrent()
+
+ assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.NotPinned)
+ }
+
+ @Test
+ fun statusBarHeadsUpState_whenShadeExpanded_false() =
+ testScope.runTest {
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+ runCurrent()
// AND the shade is expanded
shadeTestUtil.setShadeExpansion(1.0f)
+ // Needed if SceneContainer flag is off: `ShadeTestUtil.setShadeExpansion(1f)`
+ // incorrectly causes `ShadeInteractor.isShadeFullyCollapsed` to emit `true`, when it
+ // should emit `false`.
+ kosmos.fakeShadeRepository.setLegacyShadeExpansion(1.0f)
- assertThat(showHeadsUpStatusBar).isFalse()
+ assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
}
@Test
- fun showHeadsUpStatusBar_notificationsAreHidden_false() =
+ fun statusBarHeadsUpState_notificationsAreHidden_false() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+ runCurrent()
// AND the notifications are hidden
keyguardViewStateRepository.areNotificationsFullyHidden.value = true
- assertThat(showHeadsUpStatusBar).isFalse()
+ assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
}
@Test
- fun showHeadsUpStatusBar_onLockScreen_false() =
+ fun statusBarHeadsUpState_onLockScreen_false() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+ runCurrent()
// AND the lock screen is shown
keyguardTransitionRepository.emitInitialStepsFromOff(
to = KeyguardState.LOCKSCREEN,
testSetup = true,
)
- assertThat(showHeadsUpStatusBar).isFalse()
+ assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
}
@Test
- fun showHeadsUpStatusBar_onByPassLockScreen_true() =
+ fun statusBarHeadsUpState_onByPassLockScreen_true() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+ runCurrent()
// AND the lock screen is shown
keyguardTransitionRepository.emitInitialStepsFromOff(
to = KeyguardState.LOCKSCREEN,
@@ -487,13 +513,13 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() {
// AND bypass is enabled
faceAuthRepository.isBypassEnabled.value = true
- assertThat(showHeadsUpStatusBar).isTrue()
+ assertThat(statusBarHeadsUpState!!.isPinned).isTrue()
}
@Test
- fun showHeadsUpStatusBar_onByPassLockScreen_withoutNotifications_false() =
+ fun statusBarHeadsUpState_onByPassLockScreen_withoutNotifications_false() =
testScope.runTest {
- val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+ val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
// WHEN no pinned rows
// AND the lock screen is shown
@@ -504,7 +530,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() {
// AND bypass is enabled
faceAuthRepository.isBypassEnabled.value = true
- assertThat(showHeadsUpStatusBar).isFalse()
+ assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
}
private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index c9ca67e6af94..615f4b01df9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -78,7 +78,7 @@ public class FooterViewTest extends SysuiTestCase {
public void setUp() {
if (NotifRedesignFooter.isEnabled()) {
mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
- R.layout.status_bar_notification_footer_redesign, null, false);
+ R.layout.notification_2025_footer, null, false);
} else {
mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
R.layout.status_bar_notification_footer, null, false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
deleted file mode 100644
index d2a7c61d1b0d..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2019 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.headsup
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.app.Person
-import android.os.Handler
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.KosmosJavaAdapter
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.res.R
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.concurrency.mockExecutorHandler
-import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.settings.FakeGlobalSettings
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.eq
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWithLooper
-@RunWith(ParameterizedAndroidJunit4::class)
-// TODO(b/378142453): Merge this with HeadsUpManagerImplTest.
-open class HeadsUpManagerImplOldTest(flags: FlagsParameterization?) : SysuiTestCase() {
- protected var mKosmos: KosmosJavaAdapter = KosmosJavaAdapter(this)
-
- @JvmField @Rule var rule: MockitoRule = MockitoJUnit.rule()
-
- private val mUiEventLoggerFake = UiEventLoggerFake()
-
- private val mLogger: HeadsUpManagerLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
-
- @Mock private val mBgHandler: Handler? = null
-
- @Mock private val dumpManager: DumpManager? = null
-
- @Mock private val mShadeInteractor: ShadeInteractor? = null
- private var mAvalancheController: AvalancheController? = null
-
- @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
-
- protected val mGlobalSettings: FakeGlobalSettings = FakeGlobalSettings()
- protected val mSystemClock: FakeSystemClock = FakeSystemClock()
- protected val mExecutor: FakeExecutor = FakeExecutor(mSystemClock)
-
- @Mock protected var mRow: ExpandableNotificationRow? = null
-
- private fun createHeadsUpManager(): HeadsUpManagerImpl {
- return HeadsUpManagerImpl(
- mContext,
- mLogger,
- mKosmos.statusBarStateController,
- mKosmos.keyguardBypassController,
- GroupMembershipManagerImpl(),
- mKosmos.visualStabilityProvider,
- mKosmos.configurationController,
- mockExecutorHandler(mExecutor),
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityMgr,
- mUiEventLoggerFake,
- JavaAdapter(mKosmos.testScope),
- mShadeInteractor,
- mAvalancheController,
- )
- }
-
- private fun createStickyEntry(id: Int): NotificationEntry {
- val notif =
- Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(
- Mockito.mock(PendingIntent::class.java), /* highPriority */
- true,
- )
- .build()
- return HeadsUpManagerTestUtil.createEntry(id, notif)
- }
-
- private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
- val notif =
- Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
- .build()
- return HeadsUpManagerTestUtil.createEntry(id, notif)
- }
-
- private fun useAccessibilityTimeout(use: Boolean) {
- if (use) {
- Mockito.doReturn(TEST_A11Y_AUTO_DISMISS_TIME)
- .`when`(mAccessibilityMgr!!)
- .getRecommendedTimeoutMillis(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
- } else {
- Mockito.`when`(
- mAccessibilityMgr!!.getRecommendedTimeoutMillis(
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.anyInt(),
- )
- )
- .then { i: InvocationOnMock -> i.getArgument(0) }
- }
- }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
- }
-
- @Throws(Exception::class)
- override fun SysuiSetup() {
- super.SysuiSetup()
- mContext.getOrCreateTestableResources().apply {
- this.addOverride(R.integer.ambient_notification_extension_time, TEST_EXTENSION_TIME)
- this.addOverride(R.integer.touch_acceptance_delay, TEST_TOUCH_ACCEPTANCE_TIME)
- this.addOverride(
- R.integer.heads_up_notification_minimum_time,
- TEST_MINIMUM_DISPLAY_TIME,
- )
- this.addOverride(
- R.integer.heads_up_notification_minimum_time_with_throttling,
- TEST_MINIMUM_DISPLAY_TIME,
- )
- this.addOverride(R.integer.heads_up_notification_decay, TEST_AUTO_DISMISS_TIME)
- this.addOverride(
- R.integer.sticky_heads_up_notification_time,
- TEST_STICKY_AUTO_DISMISS_TIME,
- )
- }
-
- mAvalancheController =
- AvalancheController(dumpManager!!, mUiEventLoggerFake, mLogger, mBgHandler!!)
- Mockito.`when`(mShadeInteractor!!.isAnyExpanded).thenReturn(MutableStateFlow(true))
- Mockito.`when`(mKosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
- }
-
- @Test
- fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
- val bhum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- bhum.showNotification(entry)
-
- Truth.assertThat(bhum.mHeadsUpEntryMap).isNotEmpty()
- Truth.assertThat(bhum.hasNotifications()).isTrue()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testHasNotifications_avalancheMapNotEmpty_true() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isNotEmpty()
- Truth.assertThat(bhum.hasNotifications()).isTrue()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testHasNotifications_false() {
- val bhum = createHeadsUpManager()
- Truth.assertThat(bhum.mHeadsUpEntryMap).isEmpty()
- Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isEmpty()
- Truth.assertThat(bhum.hasNotifications()).isFalse()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(bhum.headsUpEntryList).contains(headsUpEntry)
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testGetHeadsUpEntry_returnsAvalancheEntry() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(bhum.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
- }
-
- @Test
- fun testShowNotification_addsEntry() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
- assertThat(alm.hasNotifications()).isTrue()
- assertThat(alm.getEntry(entry.key)).isEqualTo(entry)
- }
-
- @Test
- fun testShowNotification_autoDismisses() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
- mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
-
- assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_removeDeferred() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- val removedImmediately =
- alm.removeNotification(entry.key, /* releaseImmediately= */ false, "removeDeferred")
- assertThat(removedImmediately).isFalse()
- assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testRemoveNotification_forceRemove() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- val removedImmediately =
- alm.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
- assertThat(removedImmediately).isTrue()
- assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testReleaseAllImmediately() {
- val alm = createHeadsUpManager()
- for (i in 0 until TEST_NUM_NOTIFICATIONS) {
- val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
- entry.row = mRow
- alm.showNotification(entry)
- }
-
- alm.releaseAllImmediately()
-
- assertThat(alm.allEntries.count()).isEqualTo(0)
- }
-
- @Test
- fun testCanRemoveImmediately_notShownLongEnough() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- // The entry has just been added so we should not remove immediately.
- assertThat(alm.canRemoveImmediately(entry.key)).isFalse()
- }
-
- @Test
- fun testHunRemovedLogging() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = Mockito.mock(HeadsUpEntry::class.java)
- Mockito.`when`(headsUpEntry.pinnedStatus)
- .thenReturn(MutableStateFlow(PinnedStatus.NotPinned))
- headsUpEntry.mEntry = notifEntry
-
- hum.onEntryRemoved(headsUpEntry, "test")
-
- Mockito.verify(mLogger, Mockito.times(1)).logNotificationActuallyRemoved(eq(notifEntry))
- }
-
- @Test
- fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_autoDismissesWithDefaultTimeout() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_sticky_neverAutoDismisses() {
- val hum = createHeadsUpManager()
- val entry = createStickyEntry(/* id= */ 0)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(true)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
- useAccessibilityTimeout(true)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testRemoveNotification_beforeMinimumDisplayTime() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ false,
- "beforeMinimumDisplayTime",
- )
- assertThat(removedImmediately).isFalse()
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
-
- mSystemClock.advanceTime(
- ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_afterMinimumDisplayTime() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ false,
- "afterMinimumDisplayTime",
- )
- assertThat(removedImmediately).isTrue()
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_releaseImmediately() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(entry)
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ true,
- "afterMinimumDisplayTime",
- )
- assertThat(removedImmediately).isTrue()
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testIsSticky_rowPinnedAndExpanded_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- Mockito.`when`(mRow!!.isPinned).thenReturn(true)
- notifEntry.row = mRow
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.setExpanded(true)
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_remoteInputActive_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.mRemoteInputActive = true
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_hasFullScreenIntent_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_stickyForSomeTime_false() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
-
- hum.showNotification(entry)
-
- assertThat(hum.isSticky(entry.key)).isFalse()
- }
-
- @Test
- fun testIsSticky_false() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.setExpanded(false)
- headsUpEntry.mRemoteInputActive = false
-
- assertThat(hum.isSticky(notifEntry.key)).isFalse()
- }
-
- @Test
- fun testCompareTo_withNullEntries() {
- val hum = createHeadsUpManager()
- val alertEntry = NotificationEntryBuilder().setTag("alert").build()
-
- hum.showNotification(alertEntry)
-
- assertThat(hum.compare(alertEntry, null)).isLessThan(0)
- assertThat(hum.compare(null, alertEntry)).isGreaterThan(0)
- assertThat(hum.compare(null, null)).isEqualTo(0)
- }
-
- @Test
- fun testCompareTo_withNonAlertEntries() {
- val hum = createHeadsUpManager()
-
- val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
- val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
- val alertEntry = NotificationEntryBuilder().setTag("alert").build()
- hum.showNotification(alertEntry)
-
- assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
- assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
- assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
- }
-
- @Test
- fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
- val hum = createHeadsUpManager()
-
- val ongoingCall =
- hum.HeadsUpEntry(
- NotificationEntryBuilder()
- .setSbn(
- HeadsUpManagerTestUtil.createSbn(
- /* id = */ 0,
- Notification.Builder(mContext, "")
- .setCategory(Notification.CATEGORY_CALL)
- .setOngoing(true),
- )
- )
- .build()
- )
-
- val activeRemoteInput =
- hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
- activeRemoteInput.mRemoteInputActive = true
-
- assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
- assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
- }
-
- @Test
- fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
- val hum = createHeadsUpManager()
-
- val person = Person.Builder().setName("person").build()
- val intent = Mockito.mock(PendingIntent::class.java)
- val incomingCall =
- hum.HeadsUpEntry(
- NotificationEntryBuilder()
- .setSbn(
- HeadsUpManagerTestUtil.createSbn(
- /* id = */ 0,
- Notification.Builder(mContext, "")
- .setStyle(
- Notification.CallStyle.forIncomingCall(person, intent, intent)
- ),
- )
- )
- .build()
- )
-
- val activeRemoteInput =
- hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
- activeRemoteInput.mRemoteInputActive = true
-
- assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
- assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testPinEntry_logsPeek_throttleEnabled() {
- val hum = createHeadsUpManager()
-
- // Needs full screen intent in order to be pinned
- val entryToPin =
- hum.HeadsUpEntry(
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
- )
-
- // Note: the standard way to show a notification would be calling showNotification rather
- // than onAlertEntryAdded. However, in practice showNotification in effect adds
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin)
-
- assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(2)
- assertThat(
- AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
- ).isEqualTo(mUiEventLoggerFake.eventId(0))
- assertThat(
- HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
- ).isEqualTo(mUiEventLoggerFake.eventId(1))
- }
-
- @Test
- @DisableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testPinEntry_logsPeek_throttleDisabled() {
- val hum = createHeadsUpManager()
-
- // Needs full screen intent in order to be pinned
- val entryToPin =
- hum.HeadsUpEntry(
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
- )
-
- // Note: the standard way to show a notification would be calling showNotification rather
- // than onAlertEntryAdded. However, in practice showNotification in effect adds
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin)
-
- assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1)
- assertThat(
- HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
- ).isEqualTo(mUiEventLoggerFake.eventId(0))
- }
-
- @Test
- fun testSetUserActionMayIndirectlyRemove() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- assertThat(hum.canRemoveImmediately(notifEntry.key)).isFalse()
-
- hum.setUserActionMayIndirectlyRemove(notifEntry)
-
- assertThat(hum.canRemoveImmediately(notifEntry.key)).isTrue()
- }
-
- companion object {
- const val TEST_TOUCH_ACCEPTANCE_TIME: Int = 200
- const val TEST_A11Y_AUTO_DISMISS_TIME: Int = 1000
- const val TEST_EXTENSION_TIME = 500
-
- const val TEST_MINIMUM_DISPLAY_TIME: Int = 400
- const val TEST_AUTO_DISMISS_TIME: Int = 600
- const val TEST_STICKY_AUTO_DISMISS_TIME: Int = 800
-
- // Number of notifications to use in tests requiring multiple notifications
- private const val TEST_NUM_NOTIFICATIONS = 4
-
- init {
- Truth.assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
- Truth.assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
- Truth.assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
- }
-
- @get:Parameters(name = "{0}")
- @JvmStatic
- val flags: List<FlagsParameterization>
- get() = FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 96f0201f4596..739a9c956178 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -15,25 +15,38 @@
*/
package com.android.systemui.statusbar.notification.headsup
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
import android.os.Handler
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.accessibility.accessibilityManager
import android.view.accessibility.accessibilityManagerWrapper
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.dumpManager
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.StatusBarState
+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.collection.provider.visualStabilityProvider
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.policy.configurationController
@@ -41,14 +54,19 @@ import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.mockExecutorHandler
import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -56,20 +74,27 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper
-class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplOldTest(flags) {
-
- private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer())
+class HeadsUpManagerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
- private val mGroupManager = mock<GroupMembershipManager>()
- private val mBgHandler = mock<Handler>()
+ private val groupManager = mock<GroupMembershipManager>()
+ private val bgHandler = mock<Handler>()
+ private val headsUpManagerLogger = mock<HeadsUpManagerLogger>()
val statusBarStateController = kosmos.sysuiStatusBarStateController
- private val mJavaAdapter: JavaAdapter = JavaAdapter(testScope.backgroundScope)
+ private val globalSettings = kosmos.fakeGlobalSettings
+ private val systemClock = kosmos.fakeSystemClock
+ private val executor = kosmos.fakeExecutor
+ private val uiEventLoggerFake = kosmos.uiEventLoggerFake
+ private val javaAdapter: JavaAdapter = JavaAdapter(testScope.backgroundScope)
- private lateinit var mAvalancheController: AvalancheController
+ private lateinit var testHelper: NotificationTestHelper
+ private lateinit var avalancheController: AvalancheController
private lateinit var underTest: HeadsUpManagerImpl
@Before
@@ -92,48 +117,382 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
)
}
+ allowTestableLooperAsMainThread()
+ testHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+
whenever(kosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
kosmos.visualStabilityProvider.isReorderingAllowed = true
- mAvalancheController =
+ avalancheController =
AvalancheController(
kosmos.dumpManager,
- kosmos.uiEventLoggerFake,
- mHeadsUpManagerLogger,
- mBgHandler,
+ uiEventLoggerFake,
+ headsUpManagerLogger,
+ bgHandler,
)
underTest =
HeadsUpManagerImpl(
mContext,
- mHeadsUpManagerLogger,
+ headsUpManagerLogger,
statusBarStateController,
kosmos.keyguardBypassController,
- mGroupManager,
+ groupManager,
kosmos.visualStabilityProvider,
kosmos.configurationController,
- mockExecutorHandler(mExecutor),
- mGlobalSettings,
- mSystemClock,
- mExecutor,
+ mockExecutorHandler(executor),
+ globalSettings,
+ systemClock,
+ executor,
kosmos.accessibilityManagerWrapper,
- kosmos.uiEventLoggerFake,
- mJavaAdapter,
+ uiEventLoggerFake,
+ javaAdapter,
kosmos.shadeInteractor,
- mAvalancheController,
+ avalancheController,
)
}
@Test
- fun testSnooze() {
+ fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
underTest.showNotification(entry)
- underTest.snooze()
- Assert.assertTrue(underTest.isSnoozed(entry.sbn.packageName))
+
+ assertThat(underTest.mHeadsUpEntryMap).isNotEmpty()
+ assertThat(underTest.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_avalancheMapNotEmpty_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(avalancheController.getWaitingEntryList()).isNotEmpty()
+ assertThat(underTest.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_false() {
+ assertThat(underTest.mHeadsUpEntryMap).isEmpty()
+ assertThat(avalancheController.getWaitingEntryList()).isEmpty()
+ assertThat(underTest.hasNotifications()).isFalse()
+ }
+
+ @Test
+ fun pinnedHeadsUpStatuses_noHeadsUp() {
+ assertThat(underTest.hasPinnedHeadsUp()).isFalse()
+ assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.NotPinned)
+ }
+
+ @Test
+ fun pinnedHeadsUpStatuses_pinnedBySystem() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ entry.row = testHelper.createRow()
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ assertThat(underTest.hasPinnedHeadsUp()).isTrue()
+ assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.PinnedBySystem)
+ }
+
+ @Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun pinnedHeadsUpStatuses_pinnedByUser_butFlagOff_returnsNotPinned() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ entry.row = testHelper.createRow()
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ assertThat(underTest.hasPinnedHeadsUp()).isFalse()
+ assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.NotPinned)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun pinnedHeadsUpStatuses_pinnedByUser_flagOn() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ entry.row = testHelper.createRow()
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ assertThat(underTest.hasPinnedHeadsUp()).isTrue()
+ assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.PinnedByUser)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(underTest.headsUpEntryList).contains(headsUpEntry)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntry_returnsAvalancheEntry() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(underTest.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
+ }
+
+ @Test
+ fun testShowNotification_notPinnedByUser_addsEntry() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ assertThat(underTest.hasNotifications()).isTrue()
+ assertThat(underTest.getEntry(entry.key)).isEqualTo(entry)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testShowNotification_isPinnedByUser_addsEntry() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ assertThat(underTest.hasNotifications()).isTrue()
+ assertThat(underTest.getEntry(entry.key)).isEqualTo(entry)
+ }
+
+ @Test
+ fun testShowNotification_notPinnedByUser_autoDismisses() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = false)
+ systemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testShowNotification_isPinnedByUser_autoDismisses() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = true)
+ systemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_notPinnedByUser_removeDeferred() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately= */ false,
+ "removeDeferred",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testRemoveNotification_isPinnedByUser_removeDeferred() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately= */ false,
+ "removeDeferred",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testRemoveNotification_notPinnedByUser_forceRemove() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ val removedImmediately =
+ underTest.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testRemoveNotification_isPinnedByUser_forceRemove() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ val removedImmediately =
+ underTest.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testReleaseAllImmediately() {
+ for (i in 0 until 4) {
+ val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
+ entry.row = testHelper.createRow()
+ val isPinnedByUser = i % 2 == 0
+ underTest.showNotification(entry, isPinnedByUser)
+ }
+
+ underTest.releaseAllImmediately()
+
+ assertThat(underTest.allEntries.count()).isEqualTo(0)
+ }
+
+ @Test
+ fun testCanRemoveImmediately_notShownLongEnough_notPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ // The entry has just been added so we should not remove immediately.
+ assertThat(underTest.canRemoveImmediately(entry.key)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testCanRemoveImmediately_notShownLongEnough_isPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ // The entry has just been added so we should not remove immediately.
+ assertThat(underTest.canRemoveImmediately(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testHunRemovedLogging() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.HeadsUpEntry(notifEntry)
+ headsUpEntry.setRowPinnedStatus(PinnedStatus.NotPinned)
+
+ underTest.onEntryRemoved(headsUpEntry, "test")
+
+ verify(headsUpManagerLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry))
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithDefaultTimeout() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_beforeMinimumDisplayTime() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "beforeMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+
+ systemClock.advanceTime(((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_afterMinimumDisplayTime() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
}
@Test
- fun testSwipedOutNotification() {
+ fun testRemoveNotification_releaseImmediately() {
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ true,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testSnooze_notPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ underTest.snooze()
+
+ assertThat(underTest.isSnoozed(entry.sbn.packageName)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testSnooze_isPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry, isPinnedByUser = true)
+
+ underTest.snooze()
+
+ assertThat(underTest.isSnoozed(entry.sbn.packageName)).isTrue()
+ }
+
+ @Test
+ fun testSwipedOutNotification_notPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry, isPinnedByUser = false)
underTest.addSwipedOutNotification(entry.key)
// Remove should succeed because the notification is swiped out
@@ -143,8 +502,26 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
/* releaseImmediately= */ false,
/* reason= */ "swipe out",
)
- Assert.assertTrue(removedImmediately)
- Assert.assertFalse(underTest.isHeadsUpEntry(entry.key))
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testSwipedOutNotification_isPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry, isPinnedByUser = true)
+ underTest.addSwipedOutNotification(entry.key)
+
+ // Remove should succeed because the notification is swiped out
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately= */ false,
+ /* reason= */ "swipe out",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
}
@Test
@@ -154,7 +531,7 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
underTest.addSwipedOutNotification(entry.key)
// Notification is swiped so it can be immediately removed.
- Assert.assertTrue(underTest.canRemoveImmediately(entry.key))
+ assertThat(underTest.canRemoveImmediately(entry.key)).isTrue()
}
@Ignore("b/141538055")
@@ -162,21 +539,35 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
fun testCanRemoveImmediately_notTopEntry() {
val earlierEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
val laterEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext)
- laterEntry.row = mRow
+ laterEntry.row = mock<ExpandableNotificationRow>()
underTest.showNotification(earlierEntry)
underTest.showNotification(laterEntry)
// Notification is "behind" a higher priority notification so we can remove it immediately.
- Assert.assertTrue(underTest.canRemoveImmediately(earlierEntry.key))
+ assertThat(underTest.canRemoveImmediately(earlierEntry.key)).isTrue()
}
@Test
- fun testExtendHeadsUp() {
+ fun testExtendHeadsUp_notPinnedByUser() {
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- underTest.showNotification(entry)
+ underTest.showNotification(entry, isPinnedByUser = false)
+
+ underTest.extendHeadsUp()
+
+ systemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testExtendHeadsUp_isPinnedByUser() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry, isPinnedByUser = true)
+
underTest.extendHeadsUp()
- mSystemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
- Assert.assertTrue(underTest.isHeadsUpEntry(entry.key))
+
+ systemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
}
@Test
@@ -223,18 +614,134 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
kosmos.visualStabilityProvider.isReorderingAllowed = true
val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- underTest.showNotification(notifEntry)
+ underTest.showNotification(notifEntry, isPinnedByUser = false)
assertThat(notifEntry.isSeenInShade).isFalse()
}
@Test
+ fun testShowNotification_sticky_neverAutoDismisses() {
+ val entry = createStickyEntry(id = 0)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(true)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+ useAccessibilityTimeout(true)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_rowPinnedAndExpanded_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val row = testHelper.createRow()
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ notifEntry.row = row
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(true)
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_remoteInputActive_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.mRemoteInputActive = true
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_hasFullScreenIntent_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_stickyForSomeTime_false() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+
+ underTest.showNotification(entry)
+
+ assertThat(underTest.isSticky(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testIsSticky_false() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(false)
+ headsUpEntry.mRemoteInputActive = false
+
+ assertThat(underTest.isSticky(notifEntry.key)).isFalse()
+ }
+
+ @Test
fun testShouldHeadsUpBecomePinned_noFSI_false() =
kosmos.runTest {
statusBarStateController.setState(StatusBarState.KEYGUARD)
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -246,12 +753,12 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
// Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
- underTest.showNotification(notifEntry)
+ underTest.showNotification(notifEntry, isPinnedByUser = false)
val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
headsUpEntry!!.mWasUnpinned = false
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(notifEntry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(notifEntry)).isTrue()
}
@Test
@@ -263,27 +770,29 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
// Add notifEntry to ANM mAlertEntries map and make it unpinned
- underTest.showNotification(notifEntry)
+ underTest.showNotification(notifEntry, isPinnedByUser = false)
val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
headsUpEntry!!.mWasUnpinned = true
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(notifEntry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(notifEntry)).isFalse()
}
@Test
+ @BrokenWithSceneContainer(381869885) // because `ShadeTestUtil.setShadeExpansion(0f)`
+ // still causes `ShadeInteractor.isAnyExpanded` to emit `true`, when it should emit `false`.
fun shouldHeadsUpBecomePinned_shadeNotExpanded_true() =
kosmos.runTest {
// GIVEN
- shadeTestUtil.setShadeExpansion(0f)
- // TODO(b/381869885): Determine why we need both of these ShadeTestUtil calls.
+ // TODO(b/381869885): We should be able to use `ShadeTestUtil.setShadeExpansion(0f)`
+ // instead.
shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(false)
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.SHADE)
// THEN
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isTrue()
}
@Test
@@ -294,7 +803,7 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -305,7 +814,7 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
statusBarStateController.setState(1207)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -318,7 +827,7 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
statusBarStateController.setState(StatusBarState.KEYGUARD)
// THEN
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isTrue()
}
@Test
@@ -331,7 +840,7 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
statusBarStateController.setState(StatusBarState.KEYGUARD)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -346,15 +855,193 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplO
statusBarStateController.setState(StatusBarState.SHADE)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
+ @Test
+ fun testCompareTo_withNullEntries() {
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+
+ underTest.showNotification(alertEntry)
+
+ assertThat(underTest.compare(alertEntry, null)).isLessThan(0)
+ assertThat(underTest.compare(null, alertEntry)).isGreaterThan(0)
+ assertThat(underTest.compare(null, null)).isEqualTo(0)
+ }
+
+ @Test
+ fun testCompareTo_withNonAlertEntries() {
+ val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
+ val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+ underTest.showNotification(alertEntry)
+
+ assertThat(underTest.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
+ assertThat(underTest.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
+ assertThat(underTest.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+ val ongoingCall =
+ underTest.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setCategory(Notification.CATEGORY_CALL)
+ .setOngoing(true),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ underTest.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
+ val person = Person.Builder().setName("person").build()
+ val intent = mock<PendingIntent>()
+ val incomingCall =
+ underTest.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setStyle(
+ Notification.CallStyle.forIncomingCall(person, intent, intent)
+ ),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ underTest.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleEnabled() {
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ underTest.HeadsUpEntry(
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+ )
+
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ underTest.onEntryAdded(entryToPin, /* requestedPinnedStatus= */ PinnedStatus.PinnedBySystem)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
+ assertThat(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId())
+ .isEqualTo(uiEventLoggerFake.eventId(0))
+ assertThat(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id)
+ .isEqualTo(uiEventLoggerFake.eventId(1))
+ }
+
+ @Test
+ @DisableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleDisabled() {
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ underTest.HeadsUpEntry(
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+ )
+
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ underTest.onEntryAdded(entryToPin, /* requestedPinnedStatus= */ PinnedStatus.PinnedBySystem)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id)
+ .isEqualTo(uiEventLoggerFake.eventId(0))
+ }
+
+ @Test
+ fun testSetUserActionMayIndirectlyRemove() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ assertThat(underTest.canRemoveImmediately(notifEntry.key)).isFalse()
+
+ underTest.setUserActionMayIndirectlyRemove(notifEntry)
+
+ assertThat(underTest.canRemoveImmediately(notifEntry.key)).isTrue()
+ }
+
+ private fun createStickyEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFullScreenIntent(mock<PendingIntent>(), /* highPriority= */ true)
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun useAccessibilityTimeout(use: Boolean) {
+ if (use) {
+ whenever(kosmos.accessibilityManager.getRecommendedTimeoutMillis(any(), any()))
+ .thenReturn(TEST_A11Y_AUTO_DISMISS_TIME)
+ } else {
+ doAnswer { it.getArgument(0) as Int }
+ .whenever(kosmos.accessibilityManager)
+ .getRecommendedTimeoutMillis(any(), any())
+ }
+ }
+
companion object {
+ const val TEST_TOUCH_ACCEPTANCE_TIME = 200
+ const val TEST_A11Y_AUTO_DISMISS_TIME = 1000
+ const val TEST_EXTENSION_TIME = 500
+
+ const val TEST_MINIMUM_DISPLAY_TIME = 400
+ const val TEST_AUTO_DISMISS_TIME = 600
+ const val TEST_STICKY_AUTO_DISMISS_TIME = 800
+
+ init {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
+ }
+
@get:Parameters(name = "{0}")
+ @JvmStatic
val flags: List<FlagsParameterization>
get() = buildList {
addAll(
- FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+ FlagsParameterization.allCombinationsOf(
+ NotificationThrottleHun.FLAG_NAME,
+ StatusBarNotifChips.FLAG_NAME,
+ )
.andSceneContainer()
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 46c360aecd48..be20bc1bf9d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -225,16 +225,12 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
val displayId = 123
darkIconRepository.darkState(displayId).value =
SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC)
- val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
- assertThat(iconColorsLookup).isNotNull()
-
- val iconColors = iconColorsLookup?.iconColors(Rect())
+ val iconColors by collectLastValue(underTest.iconColors(displayId))
assertThat(iconColors).isNotNull()
- iconColors!!
- assertThat(iconColors.tint).isEqualTo(0xAABBCC)
+ assertThat(iconColors!!.tint).isEqualTo(0xAABBCC)
- val staticDrawableColor = iconColors.staticDrawableColor(Rect())
+ val staticDrawableColor = iconColors!!.staticDrawableColor(Rect())
assertThat(staticDrawableColor).isEqualTo(0xAABBCC)
}
@@ -245,8 +241,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
val displayId = 321
darkIconRepository.darkState(displayId).value =
SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
- val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
- val iconColors = iconColorsLookup?.iconColors(Rect(1, 1, 4, 4))
+ val iconColors by collectLastValue(underTest.iconColors(displayId))
val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7))
assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
}
@@ -257,9 +252,9 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
val displayId = 987
darkIconRepository.darkState(displayId).value =
SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
- val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
- val iconColors = iconColorsLookup?.iconColors(Rect(6, 6, 7, 7))
- assertThat(iconColors).isNull()
+ val iconColors by collectLastValue(underTest.iconColors(displayId))
+ assertThat(iconColors!!.staticDrawableColor(Rect(6, 6, 7, 7)))
+ .isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index af2789b8401a..f7673da6dfb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -75,8 +75,8 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
return new CancellationSignal();
});
- mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq(mEntry));
+ mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+ verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
@@ -85,8 +85,8 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
- mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq(mEntry));
+ mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ true, null);
+ verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ true);
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
@@ -116,8 +116,8 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
return new CancellationSignal();
});
- mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq(mEntry));
+ mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+ verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
@@ -140,8 +140,8 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
return new CancellationSignal();
});
- mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq(mEntry));
+ mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ true, null);
+ verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ true);
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
@@ -167,8 +167,8 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
return new CancellationSignal();
});
- mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq(mEntry));
+ mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+ verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
index 6736ccf739ce..abd0a284ae3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
@@ -101,6 +101,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
setSubText(TEST_SUB_TEXT)
setContentTitle(TEST_CONTENT_TITLE)
setContentText(TEST_CONTENT_TEXT)
+ setShortCriticalText(TEST_SHORT_CRITICAL_TEXT)
}
.also { provider.promotedEntries.add(it) }
@@ -114,6 +115,52 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+ fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
+ val entry =
+ createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.text).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ PromotedNotificationUi.FLAG_NAME,
+ StatusBarNotifChips.FLAG_NAME,
+ android.app.Flags.FLAG_API_RICH_ONGOING,
+ )
+ fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
+ val entry =
+ createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
+ }
+
+ @Test
+ @EnableFlags(
+ PromotedNotificationUi.FLAG_NAME,
+ StatusBarNotifChips.FLAG_NAME,
+ android.app.Flags.FLAG_API_RICH_ONGOING,
+ )
+ fun extractContent_noShortCriticalTextSet_textIsNull() {
+ val entry = createEntry {}.also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.shortCriticalText).isNull()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBigPictureStyle() {
val entry =
createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) }
@@ -201,6 +248,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
private const val TEST_SUB_TEXT = "sub text"
private const val TEST_CONTENT_TITLE = "content title"
private const val TEST_CONTENT_TEXT = "content text"
+ private const val TEST_SHORT_CRITICAL_TEXT = "short"
private const val TEST_PERSON_NAME = "person name"
private const val TEST_PERSON_KEY = "person key"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
index 9f98fd4c8508..5a5ec90a5f44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
@@ -21,9 +21,8 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.FakeShadowView
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.SourceType
@@ -62,8 +61,8 @@ class ActivatableNotificationViewTest : SysuiTestCase() {
} as T?
}
- mNormalColor = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorSurfaceContainerHigh)
+ mNormalColor =
+ mContext.getColor(com.android.internal.R.color.materialColorSurfaceContainerHigh)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index f76f1ce48a5c..7f139bd69a37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -30,6 +30,8 @@ import android.content.ComponentName;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.Vibrator;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.HapticFeedbackConstants;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -45,6 +47,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
@@ -54,15 +58,14 @@ import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.DualShade;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,6 +75,8 @@ import org.mockito.stubbing.Answer;
import java.util.Optional;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@@ -105,6 +110,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private ActivityStarter mActivityStarter;
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private KeyguardInteractor mKeyguardInteractor;
+ @Mock private QSPanelController mQSPanelController;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -150,6 +156,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
.thenAnswer((Answer<Integer>) invocation -> invocation.getArgument(0));
+ when(mCentralSurfaces.getQSPanelController()).thenReturn(mQSPanelController);
}
@Test
@@ -230,4 +237,45 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
verify(mQSHost).addTile(c, false);
}
+
+ @Test
+ @DisableFlags(value = {QSComposeFragment.FLAG_NAME, DualShade.FLAG_NAME})
+ public void clickQsTile_flagsDisabled_callsQSPanelController() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController).clickTile(c);
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void clickQsTile_onlyQSComposeFlag_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void clickQsTile_onlyDualShadeFlag_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
+
+ @Test
+ @EnableFlags(value = {QSComposeFragment.FLAG_NAME, DualShade.FLAG_NAME})
+ public void clickQsTile_qsComposeAndDualShadeFlags_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
index 942ea65ec49e..e87077db8e75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
@@ -21,11 +21,13 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Configuration.UI_MODE_TYPE_CAR
import android.os.LocaleList
+import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
@@ -34,7 +36,6 @@ import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import java.util.Locale
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -64,9 +65,11 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
mConfigurationController.addCallback(listener2)
doAnswer {
- mConfigurationController.removeCallback(listener2)
- null
- }.`when`(listener).onThemeChanged()
+ mConfigurationController.removeCallback(listener2)
+ null
+ }
+ .`when`(listener)
+ .onThemeChanged()
mConfigurationController.notifyThemeChanged()
verify(listener).onThemeChanged()
@@ -208,7 +211,6 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
assertThat(listener.maxBoundsChanged).isTrue()
}
-
@Test
fun localeListChanged_listenerNotified() {
val config = mContext.resources.configuration
@@ -289,7 +291,6 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
assertThat(listener.orientationChanged).isTrue()
}
-
@Test
fun multipleUpdates_listenerNotifiedOfAll() {
val config = mContext.resources.configuration
@@ -313,6 +314,17 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
}
@Test
+ fun onMovedToDisplay_dispatchedToChildren() {
+ val config = mContext.resources.configuration
+ val listener = createAndAddListener()
+
+ mConfigurationController.dispatchOnMovedToDisplay(newDisplayId = 1, config)
+
+ assertThat(listener.display).isEqualTo(1)
+ assertThat(listener.changedConfig).isEqualTo(config)
+ }
+
+ @Test
@Ignore("b/261408895")
fun equivalentConfigObject_listenerNotNotified() {
val config = mContext.resources.configuration
@@ -343,35 +355,49 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
var localeListChanged = false
var layoutDirectionChanged = false
var orientationChanged = false
+ var display = Display.DEFAULT_DISPLAY
override fun onConfigChanged(newConfig: Configuration?) {
changedConfig = newConfig
}
+
override fun onDensityOrFontScaleChanged() {
densityOrFontScaleChanged = true
}
+
override fun onSmallestScreenWidthChanged() {
smallestScreenWidthChanged = true
}
+
override fun onMaxBoundsChanged() {
maxBoundsChanged = true
}
+
override fun onUiModeChanged() {
uiModeChanged = true
}
+
override fun onThemeChanged() {
themeChanged = true
}
+
override fun onLocaleListChanged() {
localeListChanged = true
}
+
override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
layoutDirectionChanged = true
}
+
override fun onOrientationChanged(orientation: Int) {
orientationChanged = true
}
+ override fun onMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration?) {
+ display = newDisplayId
+ changedConfig = newConfiguration
+ }
+
fun assertNoMethodsCalled() {
assertThat(densityOrFontScaleChanged).isFalse()
assertThat(smallestScreenWidthChanged).isFalse()
@@ -391,6 +417,7 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
themeChanged = false
localeListChanged = false
layoutDirectionChanged = false
+ display = Display.DEFAULT_DISPLAY
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
index d017402675bf..f4c254562420 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone
import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.view.View
@@ -23,24 +24,29 @@ import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.DarkIconDispatcher
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeDarkIconDispatcher
+import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.shade.ShadeHeadsUpTracker
-import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.shade.shadeViewController
import com.android.systemui.statusbar.HeadsUpStatusBarView
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
+import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.headsup.headsUpManager
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.Before
@@ -56,146 +62,248 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class HeadsUpAppearanceControllerTest : SysuiTestCase() {
- private val mStackScrollerController: NotificationStackScrollLayoutController =
- mock<NotificationStackScrollLayoutController>()
- private val mShadeViewController: ShadeViewController = mock<ShadeViewController>()
- private val mShadeHeadsUpTracker: ShadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
- private val mDarkIconDispatcher: DarkIconDispatcher = mock<DarkIconDispatcher>()
- private var mHeadsUpAppearanceController: HeadsUpAppearanceController? = null
- private var mTestHelper: NotificationTestHelper? = null
- private var mRow: ExpandableNotificationRow? = null
- private var mEntry: NotificationEntry? = null
- private var mHeadsUpStatusBarView: HeadsUpStatusBarView? = null
- private var mHeadsUpManager: HeadsUpManager? = null
- private var mOperatorNameView: View? = null
- private var mStatusbarStateController: StatusBarStateController? = null
- private var mPhoneStatusBarTransitions: PhoneStatusBarTransitions? = null
- private var mBypassController: KeyguardBypassController? = null
- private var mWakeUpCoordinator: NotificationWakeUpCoordinator? = null
- private var mKeyguardStateController: KeyguardStateController? = null
- private var mCommandQueue: CommandQueue? = null
- private var mNotificationRoundnessManager: NotificationRoundnessManager? = null
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val stackScrollerController = mock<NotificationStackScrollLayoutController>()
+ private val shadeViewController = kosmos.shadeViewController
+ private val shadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
+ private val darkIconDispatcher = kosmos.fakeDarkIconDispatcher
+ private val statusBarStateController = kosmos.statusBarStateController
+ private val phoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
+ private val bypassController = kosmos.keyguardBypassController
+ private val wakeUpCoordinator = kosmos.notificationWakeUpCoordinator
+ private val keyguardStateController = kosmos.keyguardStateController
+ private val commandQueue = kosmos.commandQueue
+ private val notificationRoundnessManager = mock<NotificationRoundnessManager>()
+ private var headsUpManager = kosmos.headsUpManager
+
+ private lateinit var testHelper: NotificationTestHelper
+ private lateinit var row: ExpandableNotificationRow
+ private lateinit var entry: NotificationEntry
+ private lateinit var headsUpStatusBarView: HeadsUpStatusBarView
+ private lateinit var operatorNameView: View
+
+ private lateinit var underTest: HeadsUpAppearanceController
@Before
@Throws(Exception::class)
fun setUp() {
allowTestableLooperAsMainThread()
- mTestHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
- mRow = mTestHelper!!.createRow()
- mEntry = mRow!!.entry
- mHeadsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
- mHeadsUpManager = mock<HeadsUpManager>()
- mOperatorNameView = View(mContext)
- mStatusbarStateController = mock<StatusBarStateController>()
- mPhoneStatusBarTransitions = mock<PhoneStatusBarTransitions>()
- mBypassController = mock<KeyguardBypassController>()
- mWakeUpCoordinator = mock<NotificationWakeUpCoordinator>()
- mKeyguardStateController = mock<KeyguardStateController>()
- mCommandQueue = mock<CommandQueue>()
- mNotificationRoundnessManager = mock<NotificationRoundnessManager>()
- whenever(mShadeViewController.shadeHeadsUpTracker).thenReturn(mShadeHeadsUpTracker)
- mHeadsUpAppearanceController =
+ testHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ row = testHelper.createRow()
+ entry = row.entry
+ headsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
+ operatorNameView = View(mContext)
+
+ whenever(shadeViewController.shadeHeadsUpTracker).thenReturn(shadeHeadsUpTracker)
+ underTest =
HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
+ headsUpManager,
+ statusBarStateController,
+ phoneStatusBarTransitions,
+ bypassController,
+ wakeUpCoordinator,
+ darkIconDispatcher,
+ keyguardStateController,
+ commandQueue,
+ stackScrollerController,
+ shadeViewController,
+ notificationRoundnessManager,
+ headsUpStatusBarView,
Clock(mContext, null),
- mock<HeadsUpNotificationIconInteractor>(),
- Optional.of(mOperatorNameView!!),
+ kosmos.headsUpNotificationIconInteractor,
+ Optional.of(operatorNameView),
)
- mHeadsUpAppearanceController!!.setAppearFraction(0.0f, 0.0f)
+ underTest.setAppearFraction(0.0f, 0.0f)
+ }
+
+ @Test
+ fun showingEntryUpdated_whenPinnedBySystem() {
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(headsUpStatusBarView.showingEntry).isEqualTo(row.entry)
+
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ setHeadsUpNotifOnManager(null)
+ underTest.onHeadsUpUnPinned(entry)
+
+ assertThat(headsUpStatusBarView.showingEntry).isNull()
+ }
+
+ @Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun showingEntryUpdated_whenPinnedByUser_andFlagOff() {
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(headsUpStatusBarView.showingEntry).isEqualTo(row.entry)
}
@Test
- fun testShowinEntryUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mHeadsUpStatusBarView!!.showingEntry).isEqualTo(mRow!!.entry)
-
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mHeadsUpStatusBarView!!.showingEntry).isNull()
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun showingEntryNotUpdated_whenPinnedByUser_andFlagOn() {
+ // WHEN the HUN was pinned by the user
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ // THEN we don't show the HUN status bar view
+ assertThat(headsUpStatusBarView.showingEntry).isNull()
}
@Test
- fun testPinnedStatusUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mHeadsUpAppearanceController!!.pinnedStatus)
- .isEqualTo(PinnedStatus.PinnedBySystem)
-
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mHeadsUpAppearanceController!!.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+ fun pinnedStatusUpdatedToSystem_whenPinnedBySystem() {
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.PinnedBySystem)
+
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ setHeadsUpNotifOnManager(null)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
}
@Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun pinnedStatusUpdatedToNotPinned_whenPinnedByUser_andFlagOn() {
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ // It's unintuitive that the pinnedStatus wouldn't match the status on the notification.
+ // Explanation: HeadsUpAppearanceController#updateTopEntry doesn't do anything if
+ // HeadsUpManager.pinnedHeadsUpStatus != PinnedBySystem. So when we're PinnedByUser,
+ // HeadsUpAppearanceController early-returns before even updating the pinned status.
+ assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+ }
+
+ @Test
+ fun isolatedIconSet_whenPinnedBySystem() =
+ kosmos.runTest {
+ val latestIsolatedIcon by
+ collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(latestIsolatedIcon).isEqualTo(entry.key)
+
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ setHeadsUpNotifOnManager(null)
+ underTest.onHeadsUpUnPinned(entry)
+
+ assertThat(latestIsolatedIcon).isNull()
+ }
+
+ @Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun isolatedIconSet_whenPinnedByUser_andFlagOff() =
+ kosmos.runTest {
+ val latestIsolatedIcon by
+ collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(latestIsolatedIcon).isEqualTo(entry.key)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun isolatedIconNotSet_whenPinnedByUser_andFlagOn() =
+ kosmos.runTest {
+ val latestIsolatedIcon by
+ collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(latestIsolatedIcon).isNull()
+ }
+
+ @Test
@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
fun testHeaderUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mRow!!.headerVisibleAmount).isEqualTo(0.0f)
-
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mRow!!.headerVisibleAmount).isEqualTo(1.0f)
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(row.headerVisibleAmount).isEqualTo(0.0f)
+
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ setHeadsUpNotifOnManager(null)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(row.headerVisibleAmount).isEqualTo(1.0f)
}
@Test
- fun testOperatorNameViewUpdated() {
- mHeadsUpAppearanceController!!.setAnimationsEnabled(false)
-
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mOperatorNameView!!.visibility).isEqualTo(View.INVISIBLE)
-
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mOperatorNameView!!.visibility).isEqualTo(View.VISIBLE)
+ fun operatorNameViewUpdated_whenPinnedBySystem() {
+ underTest.setAnimationsEnabled(false)
+
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(operatorNameView.visibility).isEqualTo(View.INVISIBLE)
+
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ setHeadsUpNotifOnManager(null)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(operatorNameView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun operatorNameViewUpdated_whenPinnedByUser_andFlagOff() {
+ underTest.setAnimationsEnabled(false)
+
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ assertThat(operatorNameView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun operatorNameViewNotUpdated_whenPinnedByUser_andFlagOn() {
+ underTest.setAnimationsEnabled(false)
+
+ // WHEN the row was pinned by the user
+ row.setPinnedStatus(PinnedStatus.PinnedByUser)
+ setHeadsUpNotifOnManager(entry)
+ underTest.onHeadsUpPinned(entry)
+
+ // THEN we don't need to hide the operator name view
+ assertThat(operatorNameView.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun constructor_animationValuesUpdated() {
val appearFraction = .75f
val expandedHeight = 400f
- whenever(mStackScrollerController.appearFraction).thenReturn(appearFraction)
- whenever(mStackScrollerController.expandedHeight).thenReturn(expandedHeight)
+ whenever(stackScrollerController.appearFraction).thenReturn(appearFraction)
+ whenever(stackScrollerController.expandedHeight).thenReturn(expandedHeight)
val newController =
HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
+ headsUpManager,
+ statusBarStateController,
+ phoneStatusBarTransitions,
+ bypassController,
+ wakeUpCoordinator,
+ darkIconDispatcher,
+ keyguardStateController,
+ commandQueue,
+ stackScrollerController,
+ shadeViewController,
+ notificationRoundnessManager,
+ headsUpStatusBarView,
Clock(mContext, null),
mock<HeadsUpNotificationIconInteractor>(),
Optional.empty(),
@@ -207,77 +315,88 @@ class HeadsUpAppearanceControllerTest : SysuiTestCase() {
@Test
fun testDestroy() {
- reset(mHeadsUpManager)
- reset(mDarkIconDispatcher)
- reset(mShadeHeadsUpTracker)
- reset(mStackScrollerController)
-
- mHeadsUpAppearanceController!!.onViewDetached()
-
- verify(mHeadsUpManager!!).removeListener(any())
- verify(mDarkIconDispatcher).removeDarkReceiver(any())
- verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
- verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(null)
- verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any())
+ reset(headsUpManager)
+ reset(shadeHeadsUpTracker)
+ reset(stackScrollerController)
+
+ underTest.onViewDetached()
+
+ verify(headsUpManager).removeListener(any())
+ assertThat(darkIconDispatcher.receivers).isEmpty()
+ verify(shadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
+ verify(shadeHeadsUpTracker).setHeadsUpAppearanceController(null)
+ verify(stackScrollerController).removeOnExpandedHeightChangedListener(any())
}
@Test
fun testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
// Pulsing: Enable flag and dozing
- whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
- whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+ whenever(notificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(testHelper.statusBarStateController.isDozing).thenReturn(true)
// Pulsing: Enabled
- mRow!!.isHeadsUp = true
- mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+ row.isHeadsUp = true
+ underTest.updateHeadsUpAndPulsingRoundness(entry)
- val debugString: String = mRow!!.roundableState.debugString()
+ val debugString: String = row.roundableState.debugString()
// If Pulsing is enabled, roundness should be set to 1
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(1.0)
assertThat(debugString).contains("Pulsing")
// Pulsing: Disabled
- mRow!!.isHeadsUp = false
- mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+ row.isHeadsUp = false
+ underTest.updateHeadsUpAndPulsingRoundness(entry)
// If Pulsing is disabled, roundness should be set to 0
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(0.0)
}
@Test
fun testPulsingRoundness_onHeadsUpStateChanged() {
// Pulsing: Enable flag and dozing
- whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
- whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+ whenever(notificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(testHelper.statusBarStateController.isDozing).thenReturn(true)
// Pulsing: Enabled
- mEntry!!.setHeadsUp(true)
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+ entry.setHeadsUp(true)
+ underTest.onHeadsUpStateChanged(entry, true)
- val debugString: String = mRow!!.roundableState.debugString()
+ val debugString: String = row.roundableState.debugString()
// If Pulsing is enabled, roundness should be set to 1
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(1.0)
assertThat(debugString).contains("Pulsing")
// Pulsing: Disabled
- mEntry!!.setHeadsUp(false)
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+ entry.setHeadsUp(false)
+ underTest.onHeadsUpStateChanged(entry, false)
// If Pulsing is disabled, roundness should be set to 0
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(0.0)
}
@Test
fun onHeadsUpStateChanged_true_transitionsNotified() {
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+ underTest.onHeadsUpStateChanged(entry, true)
- verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(true)
+ verify(phoneStatusBarTransitions).onHeadsUpStateChanged(true)
}
@Test
fun onHeadsUpStateChanged_false_transitionsNotified() {
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+ underTest.onHeadsUpStateChanged(entry, false)
+
+ verify(phoneStatusBarTransitions).onHeadsUpStateChanged(false)
+ }
- verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(false)
+ private fun setHeadsUpNotifOnManager(entry: NotificationEntry?) {
+ if (entry != null) {
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(headsUpManager.pinnedHeadsUpStatus()).thenReturn(entry.pinnedStatus)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+ } else {
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ whenever(headsUpManager.pinnedHeadsUpStatus()).thenReturn(PinnedStatus.NotPinned)
+ whenever(headsUpManager.getTopEntry()).thenReturn(null)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
deleted file mode 100644
index 41782a123f14..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ /dev/null
@@ -1,379 +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.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.InitController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shade.NotificationShadeWindowView;
-import com.android.systemui.shade.QuickSettingsController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper()
-public class StatusBarNotificationPresenterTest extends SysuiTestCase {
- private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
- private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
- mock(VisualInterruptionDecisionProvider.class);
- private NotificationInterruptSuppressor mInterruptSuppressor;
- private VisualInterruptionCondition mAlertsDisabledCondition;
- private VisualInterruptionCondition mVrModeCondition;
- private VisualInterruptionFilter mNeedsRedactionFilter;
- private VisualInterruptionCondition mPanelsDisabledCondition;
- private CommandQueue mCommandQueue;
- private final ShadeController mShadeController = mock(ShadeController.class);
- private final NotificationAlertsInteractor mNotificationAlertsInteractor =
- mock(NotificationAlertsInteractor.class);
- private final KeyguardStateController mKeyguardStateController =
- mock(KeyguardStateController.class);
-
- @Before
- public void setup() {
- mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
- mDependency.injectTestDependency(StatusBarStateController.class,
- mock(SysuiStatusBarStateController.class));
- mDependency.injectTestDependency(ShadeController.class, mShadeController);
- mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
- mDependency.injectMockDependency(NotificationShadeWindowController.class);
-
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
-
- createPresenter();
- if (VisualInterruptionRefactor.isEnabled()) {
- verifyAndCaptureSuppressors();
- } else {
- verifyAndCaptureLegacySuppressor();
- }
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testInit_refactorDisabled() {
- assertFalse(VisualInterruptionRefactor.isEnabled());
- assertNull(mAlertsDisabledCondition);
- assertNull(mVrModeCondition);
- assertNull(mNeedsRedactionFilter);
- assertNull(mPanelsDisabledCondition);
- assertNotNull(mInterruptSuppressor);
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testInit_refactorEnabled() {
- assertTrue(VisualInterruptionRefactor.isEnabled());
- assertNotNull(mAlertsDisabledCondition);
- assertNotNull(mVrModeCondition);
- assertNotNull(mNeedsRedactionFilter);
- assertNotNull(mPanelsDisabledCondition);
- assertNull(mInterruptSuppressor);
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_default_refactorDisabled() {
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_default_refactorEnabled() {
- assertFalse(mAlertsDisabledCondition.shouldSuppress());
- assertFalse(mVrModeCondition.shouldSuppress());
- assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
- assertFalse(mAlertsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress heads up while disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress heads up while disabled",
- mPanelsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress interruptions while notification shade disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress interruptions while notification shade disabled",
- mPanelsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testPanelsDisabledConditionSuppressesPeek() {
- final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertFalse(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
- assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
-
- final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertFalse(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_vrMode_refactorDisabled() {
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions",
- mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_vrMode_refactorEnabled() {
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
-
- final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertTrue(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mAlertsDisabledCondition.shouldSuppress());
-
- final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertTrue(types.contains(PULSE));
- assertTrue(types.contains(BUBBLE));
- }
-
- private void createPresenter() {
- final ShadeViewController shadeViewController = mock(ShadeViewController.class);
-
- final NotificationShadeWindowView notificationShadeWindowView =
- mock(NotificationShadeWindowView.class);
- when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
-
- NotificationStackScrollLayoutController stackScrollLayoutController =
- mock(NotificationStackScrollLayoutController.class);
- when(stackScrollLayoutController.getView()).thenReturn(
- mock(NotificationStackScrollLayout.class));
-
- final InitController initController = new InitController();
-
- mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
- mContext,
- shadeViewController,
- mock(PanelExpansionInteractor.class),
- mock(QuickSettingsController.class),
- mock(HeadsUpManager.class),
- notificationShadeWindowView,
- mock(ActivityStarter.class),
- stackScrollLayoutController,
- mock(DozeScrimController.class),
- mock(NotificationShadeWindowController.class),
- mock(DynamicPrivacyController.class),
- mKeyguardStateController,
- mNotificationAlertsInteractor,
- mock(LockscreenShadeTransitionController.class),
- mock(PowerInteractor.class),
- mCommandQueue,
- mock(NotificationLockscreenUserManager.class),
- mock(SysuiStatusBarStateController.class),
- mock(NotifShadeEventSource.class),
- mock(NotificationMediaManager.class),
- mock(NotificationGutsManager.class),
- initController,
- mVisualInterruptionDecisionProvider,
- mock(NotificationRemoteInputManager.class),
- mock(NotificationRemoteInputManager.Callback.class),
- mock(NotificationListContainer.class));
-
- initController.executePostInitTasks();
- }
-
- private void verifyAndCaptureSuppressors() {
- mInterruptSuppressor = null;
-
- final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
- ArgumentCaptor.forClass(VisualInterruptionCondition.class);
- verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
- conditionCaptor.capture());
- final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
- mAlertsDisabledCondition = conditions.get(0);
- mVrModeCondition = conditions.get(1);
- mPanelsDisabledCondition = conditions.get(2);
-
- final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
- ArgumentCaptor.forClass(VisualInterruptionFilter.class);
- verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
- mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
- }
-
- private void verifyAndCaptureLegacySuppressor() {
- mAlertsDisabledCondition = null;
- mVrModeCondition = null;
- mNeedsRedactionFilter = null;
- mPanelsDisabledCondition = null;
-
- final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
- ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
- verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
- mInterruptSuppressor = suppressorCaptor.getValue();
- }
-
- private NotificationEntry createNotificationEntry() {
- return new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(new Notification.Builder(getContext(), "a").build())
- .build();
- }
-
- private NotificationEntry createFsiNotificationEntry() {
- final Notification notification = new Notification.Builder(getContext(), "a")
- .setFullScreenIntent(mock(PendingIntent.class), true)
- .build();
-
- return new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(notification)
- .build();
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
new file mode 100644
index 000000000000..c347347eff83
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
@@ -0,0 +1,454 @@
+/*
+ * 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.phone
+
+import android.app.Notification
+import android.app.Notification.Builder
+import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.InitController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.lockscreenShadeTransitionController
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.domain.interactor.notificationAlertsInteractor
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.notificationRemoteInputManager
+import com.android.systemui.statusbar.notificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class StatusBarNotificationPresenterTest : SysuiTestCase() {
+ private lateinit var kosmos: Kosmos
+
+ private var interruptSuppressor: NotificationInterruptSuppressor? = null
+ private var alertsDisabledCondition: VisualInterruptionCondition? = null
+ private var vrModeCondition: VisualInterruptionCondition? = null
+ private var needsRedactionFilter: VisualInterruptionFilter? = null
+ private var panelsDisabledCondition: VisualInterruptionCondition? = null
+
+ private val commandQueue: CommandQueue = CommandQueue(mContext, FakeDisplayTracker(mContext))
+ private val keyguardStateController: KeyguardStateController
+ get() = kosmos.keyguardStateController
+
+ private val notificationAlertsInteractor
+ get() = kosmos.notificationAlertsInteractor
+
+ private val visualInterruptionDecisionProvider
+ get() = kosmos.visualInterruptionDecisionProvider
+
+ private lateinit var underTest: StatusBarNotificationPresenter
+
+ @Before
+ fun setup() {
+ kosmos =
+ testKosmos().apply {
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled())
+ .thenReturn(true)
+ whenever(notificationStackScrollLayoutController.expandHelperCallback)
+ .thenReturn(mock())
+ lockscreenShadeTransitionController.setStackScroller(
+ notificationStackScrollLayoutController
+ )
+ lockscreenShadeTransitionController.centralSurfaces = mock()
+ }
+
+ underTest = createPresenter()
+ if (VisualInterruptionRefactor.isEnabled) {
+ verifyAndCaptureSuppressors()
+ } else {
+ verifyAndCaptureLegacySuppressor()
+ }
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testInit_refactorDisabled() {
+ assertThat(VisualInterruptionRefactor.isEnabled).isFalse()
+ assertThat(alertsDisabledCondition).isNull()
+ assertThat(vrModeCondition).isNull()
+ assertThat(needsRedactionFilter).isNull()
+ assertThat(panelsDisabledCondition).isNull()
+ assertThat(interruptSuppressor).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testInit_refactorEnabled() {
+ assertThat(VisualInterruptionRefactor.isEnabled).isTrue()
+ assertThat(alertsDisabledCondition).isNotNull()
+ assertThat(vrModeCondition).isNotNull()
+ assertThat(needsRedactionFilter).isNotNull()
+ assertThat(panelsDisabledCondition).isNotNull()
+ assertThat(interruptSuppressor).isNull()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_default_refactorDisabled() {
+ assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry())).isFalse()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_default_refactorEnabled() {
+ assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+ assertThat(vrModeCondition!!.shouldSuppress()).isFalse()
+ assertThat(needsRedactionFilter!!.shouldSuppress(createNotificationEntry())).isFalse()
+ assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+ commandQueue.disable(
+ DEFAULT_DISPLAY,
+ StatusBarManager.DISABLE_EXPAND,
+ 0,
+ false, /* animate */
+ )
+ TestableLooper.get(this).processAllMessages()
+
+ assertWithMessage("The panel should suppress heads up while disabled")
+ .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+ commandQueue.disable(
+ DEFAULT_DISPLAY,
+ StatusBarManager.DISABLE_EXPAND,
+ 0,
+ false, /* animate */
+ )
+ TestableLooper.get(this).processAllMessages()
+
+ assertWithMessage("The panel should suppress heads up while disabled")
+ .that(panelsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+ commandQueue.disable(
+ DEFAULT_DISPLAY,
+ 0,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false, /* animate */
+ )
+ TestableLooper.get(this).processAllMessages()
+
+ assertWithMessage(
+ "The panel should suppress interruptions while notification shade disabled"
+ )
+ .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+ commandQueue.disable(
+ DEFAULT_DISPLAY,
+ 0,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false, /* animate */
+ )
+ TestableLooper.get(this).processAllMessages()
+
+ assertWithMessage(
+ "The panel should suppress interruptions while notification shade disabled"
+ )
+ .that(panelsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testPanelsDisabledConditionSuppressesPeek() {
+ val types: Set<VisualInterruptionType> = panelsDisabledCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types)
+ .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+ whenever(keyguardStateController.isShowing()).thenReturn(true)
+ whenever(keyguardStateController.isOccluded()).thenReturn(false)
+
+ assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createFsiNotificationEntry()))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+ whenever(keyguardStateController.isShowing()).thenReturn(true)
+ whenever(keyguardStateController.isOccluded()).thenReturn(false)
+
+ assertThat(needsRedactionFilter!!.shouldSuppress(createFsiNotificationEntry())).isFalse()
+
+ val types: Set<VisualInterruptionType> = needsRedactionFilter!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types)
+ .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_vrMode_refactorDisabled() {
+ underTest.mVrMode = true
+
+ assertWithMessage("Vr mode should suppress interruptions")
+ .that(interruptSuppressor!!.suppressAwakeInterruptions(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_vrMode_refactorEnabled() {
+ underTest.mVrMode = true
+
+ assertWithMessage("Vr mode should suppress interruptions")
+ .that(vrModeCondition!!.shouldSuppress())
+ .isTrue()
+
+ val types: Set<VisualInterruptionType> = vrModeCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types).doesNotContain(VisualInterruptionType.PULSE)
+ assertThat(types).contains(VisualInterruptionType.BUBBLE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+
+ assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+ .that(interruptSuppressor!!.suppressInterruptions(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+
+ assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+ .that(alertsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+
+ val types: Set<VisualInterruptionType> = alertsDisabledCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types).contains(VisualInterruptionType.PULSE)
+ assertThat(types).contains(VisualInterruptionType.BUBBLE)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testExpandSensitiveNotification_onLockScreen_opensShade() =
+ kosmos.runTest {
+ // Given we are on the keyguard
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.KEYGUARD
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val entry =
+ createRow().entry.apply {
+ setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+ }
+ underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+ // Then we open the locked shade
+ assertThat(kosmos.sysuiStatusBarStateController.state)
+ .isEqualTo(StatusBarState.SHADE_LOCKED)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testExpandSensitiveNotification_onLockedShade_showsBouncer() =
+ kosmos.runTest {
+ // Given we are on the locked shade
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.SHADE_LOCKED
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val entry =
+ createRow().entry.apply {
+ setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+ }
+ underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+ // Then we show the bouncer
+ verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
+ // AND we are still on the locked shade
+ assertThat(kosmos.sysuiStatusBarStateController.state)
+ .isEqualTo(StatusBarState.SHADE_LOCKED)
+ }
+
+ private fun createPresenter(): StatusBarNotificationPresenter {
+ val shadeViewController: ShadeViewController = mock()
+
+ val notificationShadeWindowView: NotificationShadeWindowView = mock()
+ whenever(notificationShadeWindowView.resources).thenReturn(mContext.resources)
+ whenever(kosmos.notificationStackScrollLayoutController.view).thenReturn(mock())
+
+ val initController: InitController = InitController()
+
+ return StatusBarNotificationPresenter(
+ mContext,
+ shadeViewController,
+ kosmos.panelExpansionInteractor,
+ /* quickSettingsController = */ mock(),
+ kosmos.headsUpManager,
+ notificationShadeWindowView,
+ kosmos.activityStarter,
+ kosmos.notificationStackScrollLayoutController,
+ kosmos.dozeScrimController,
+ kosmos.notificationShadeWindowController,
+ kosmos.dynamicPrivacyController,
+ kosmos.keyguardStateController,
+ kosmos.notificationAlertsInteractor,
+ kosmos.lockscreenShadeTransitionController,
+ kosmos.powerInteractor,
+ commandQueue,
+ kosmos.notificationLockscreenUserManager,
+ kosmos.sysuiStatusBarStateController,
+ /* notifShadeEventSource = */ mock(),
+ /* notificationMediaManager = */ mock(),
+ /* notificationGutsManager = */ mock(),
+ initController,
+ kosmos.visualInterruptionDecisionProvider,
+ kosmos.notificationRemoteInputManager,
+ /* remoteInputManagerCallback = */ mock(),
+ /* notificationListContainer = */ mock(),
+ kosmos.deviceUnlockedInteractor,
+ )
+ .also { initController.executePostInitTasks() }
+ }
+
+ private fun verifyAndCaptureSuppressors() {
+ interruptSuppressor = null
+
+ val conditionCaptor = argumentCaptor<VisualInterruptionCondition>()
+ verify(visualInterruptionDecisionProvider, times(3)).addCondition(conditionCaptor.capture())
+ val conditions: List<VisualInterruptionCondition> = conditionCaptor.allValues
+ alertsDisabledCondition = conditions[0]
+ vrModeCondition = conditions[1]
+ panelsDisabledCondition = conditions[2]
+
+ val needsRedactionFilterCaptor = argumentCaptor<VisualInterruptionFilter>()
+ verify(visualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture())
+ needsRedactionFilter = needsRedactionFilterCaptor.lastValue
+ }
+
+ private fun verifyAndCaptureLegacySuppressor() {
+ alertsDisabledCondition = null
+ vrModeCondition = null
+ needsRedactionFilter = null
+ panelsDisabledCondition = null
+
+ val suppressorCaptor = argumentCaptor<NotificationInterruptSuppressor>()
+ verify(visualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture())
+ interruptSuppressor = suppressorCaptor.lastValue
+ }
+
+ private fun createRow(): ExpandableNotificationRow {
+ val row: ExpandableNotificationRow = mock()
+ val entry: NotificationEntry = createNotificationEntry()
+ whenever(row.entry).thenReturn(entry)
+ entry.row = row
+ return row
+ }
+
+ private fun createNotificationEntry(): NotificationEntry =
+ NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(Builder(mContext, "a").build())
+ .build()
+
+ private fun createFsiNotificationEntry(): NotificationEntry {
+ val notification: Notification =
+ Builder(mContext, "a").setFullScreenIntent(mock(), true).build()
+
+ return NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(notification)
+ .build()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 79b5cc37119f..0652a835cb7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
@@ -120,6 +121,22 @@ public class SystemUIDialogTest extends SysuiTestCase {
}
@Test
+ public void testRegisterReceiverWithoutAcsd() {
+ SystemUIDialog dialog = createDialogWithDelegate(mContext, mDelegate,
+ false /* shouldAcsdDismissDialog */);
+ final ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ dialog.show();
+ verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverCaptor.capture(),
+ intentFilterCaptor.capture(), ArgumentMatchers.eq(null), ArgumentMatchers.any());
+ assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
+ assertFalse(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ @Test
@RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS)
public void usePredictiveBackAnimFlag() {
final SystemUIDialog dialog = new SystemUIDialog(mContext);
@@ -163,7 +180,8 @@ public class SystemUIDialogTest extends SysuiTestCase {
public void delegateIsCalled_inCorrectOrder() {
Configuration configuration = new Configuration();
InOrder inOrder = Mockito.inOrder(mDelegate);
- SystemUIDialog dialog = createDialogWithDelegate();
+ SystemUIDialog dialog = createDialogWithDelegate(mContext, mDelegate,
+ true /* shouldAcsdDismissDialog */);
dialog.show();
dialog.onWindowFocusChanged(/* hasFocus= */ true);
@@ -178,7 +196,8 @@ public class SystemUIDialogTest extends SysuiTestCase {
inOrder.verify(mDelegate).onStop(dialog);
}
- private SystemUIDialog createDialogWithDelegate() {
+ private SystemUIDialog createDialogWithDelegate(Context context,
+ SystemUIDialog.Delegate delegate, boolean shouldAcsdDismissDialog) {
SystemUIDialog.Factory factory = new SystemUIDialog.Factory(
getContext(),
Dependency.get(SystemUIDialogManager.class),
@@ -186,6 +205,6 @@ public class SystemUIDialogTest extends SysuiTestCase {
Dependency.get(BroadcastDispatcher.class),
Dependency.get(DialogTransitionAnimator.class)
);
- return factory.create(mDelegate);
+ return factory.create(delegate, context, shouldAcsdDismissDialog);
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt
index c90183df9847..1cc55bf87b1a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt
@@ -127,12 +127,6 @@ class KeyguardBypassInteractorTest : SysuiTestCase() {
kosmos.configureKeyguardBypass(isBypassAvailable = skipIsBypassAvailableCheck)
underTest = kosmos.keyguardBypassInteractor
- // bouncerShowing false, !onLockscreenScene false
- // !onLockscreenScene false
- setScene(
- bouncerShowing = !skipBouncerShowingCheck,
- onLockscreenScene = skipOnLockscreenSceneCheck,
- )
// alternateBouncerShowing false
setAlternateBouncerShowing(!skipAlternateBouncerShowingCheck)
// launchingAffordance false
@@ -141,6 +135,13 @@ class KeyguardBypassInteractorTest : SysuiTestCase() {
setPulseExpanding(!skipPulseExpandingCheck)
// qsExpanding false
setQsExpanded(!skipQsExpandedCheck)
+
+ // bouncerShowing false, !onLockscreenScene false
+ // !onLockscreenScene false
+ setScene(
+ bouncerShowing = !skipBouncerShowingCheck,
+ onLockscreenScene = skipOnLockscreenSceneCheck,
+ )
}
private fun setAlternateBouncerShowing(alternateBouncerVisible: Boolean) {
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 ca1413e48966..8fb95e843ec1 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
@@ -30,17 +30,25 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
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.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -49,6 +57,11 @@ class OngoingCallInteractorTest : SysuiTestCase() {
private val repository = kosmos.activeNotificationListRepository
private val underTest = kosmos.ongoingCallInteractor
+ @Before
+ fun setUp() {
+ underTest.start()
+ }
+
@Test
fun noNotification_emitsNoCall() = runTest {
val state by collectLastValue(underTest.ongoingCallState)
@@ -72,7 +85,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
whenTime = 1000L,
callType = CallType.Ongoing,
statusBarChipIcon = testIconView,
- contentIntent = testIntent
+ contentIntent = testIntent,
)
)
}
@@ -95,7 +108,9 @@ class OngoingCallInteractorTest : SysuiTestCase() {
.apply {
addIndividualNotif(
activeNotificationModel(
- key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
)
)
}
@@ -114,7 +129,9 @@ class OngoingCallInteractorTest : SysuiTestCase() {
.apply {
addIndividualNotif(
activeNotificationModel(
- key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
)
)
}
@@ -138,7 +155,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
key = "notif1",
whenTime = 1000L,
callType = CallType.Ongoing,
- uid = UID
+ uid = UID,
)
)
}
@@ -161,7 +178,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
key = "notif1",
whenTime = 1000L,
callType = CallType.Ongoing,
- uid = UID
+ uid = UID,
)
)
}
@@ -185,13 +202,12 @@ class OngoingCallInteractorTest : SysuiTestCase() {
key = "notif1",
whenTime = 1000L,
callType = CallType.Ongoing,
- uid = UID
+ uid = UID,
)
)
}
.build()
- assertThat(latest)
- .isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
// App becomes visible
kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
@@ -202,6 +218,176 @@ class OngoingCallInteractorTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
}
+ @Test
+ fun ongoingCallNotification_setsRequiresStatusBarVisibleTrue() =
+ kosmos.runTest {
+ val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+ val requiresStatusBarVisibleInRepository by
+ collectLastValue(
+ kosmos.fakeStatusBarModeRepository.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ val requiresStatusBarVisibleInWindowController by
+ collectLastValue(
+ kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ postOngoingCallNotification()
+
+ assertThat(isStatusBarRequired).isTrue()
+ assertThat(requiresStatusBarVisibleInRepository).isTrue()
+ assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+ }
+
+ @Test
+ fun notificationRemoved_setsRequiresStatusBarVisibleFalse() =
+ kosmos.runTest {
+ val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+ val requiresStatusBarVisibleInRepository by
+ collectLastValue(
+ kosmos.fakeStatusBarModeRepository.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ val requiresStatusBarVisibleInWindowController by
+ collectLastValue(
+ kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+
+ postOngoingCallNotification()
+
+ repository.activeNotifications.value = ActiveNotificationsStore()
+
+ assertThat(isStatusBarRequired).isFalse()
+ assertThat(requiresStatusBarVisibleInRepository).isFalse()
+ assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+ }
+
+ @Test
+ fun ongoingCallNotification_appBecomesVisible_setsRequiresStatusBarVisibleFalse() =
+ kosmos.runTest {
+ val ongoingCallState by collectLastValue(underTest.ongoingCallState)
+
+ val requiresStatusBarVisibleInRepository by
+ collectLastValue(
+ kosmos.fakeStatusBarModeRepository.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ val requiresStatusBarVisibleInWindowController by
+ collectLastValue(
+ kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+
+ kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ postOngoingCallNotification()
+
+ assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat(requiresStatusBarVisibleInRepository).isTrue()
+ assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+
+ kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
+
+ assertThat(ongoingCallState)
+ .isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+ assertThat(requiresStatusBarVisibleInRepository).isFalse()
+ assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+ }
+
+ @Test
+ fun gestureHandler_inCall_notFullscreen_doesNotListen() =
+ kosmos.runTest {
+ val ongoingCallState by collectLastValue(underTest.ongoingCallState)
+
+ clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+ // Set up notification but not in fullscreen
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
+ postOngoingCallNotification()
+
+ assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ verify(kosmos.swipeStatusBarAwayGestureHandler, never())
+ .addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun gestureHandler_inCall_fullscreen_addsListener() =
+ kosmos.runTest {
+ val isGestureListeningEnabled by collectLastValue(underTest.isGestureListeningEnabled)
+
+ // Set up notification and fullscreen mode
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+ postOngoingCallNotification()
+
+ assertThat(isGestureListeningEnabled).isTrue()
+ verify(kosmos.swipeStatusBarAwayGestureHandler)
+ .addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun gestureHandler_inCall_fullscreen_chipSwiped_removesListener() =
+ kosmos.runTest {
+ val swipeAwayState by collectLastValue(underTest.isChipSwipedAway)
+
+ // Set up notification and fullscreen mode
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+ postOngoingCallNotification()
+
+ clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+
+ underTest.onStatusBarSwiped()
+
+ assertThat(swipeAwayState).isTrue()
+ verify(kosmos.swipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(any())
+ }
+
+ @Test
+ fun chipSwipedAway_setsRequiresStatusBarVisibleFalse() =
+ kosmos.runTest {
+ val isStatusBarRequiredForOngoingCall by
+ collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+ val requiresStatusBarVisibleInRepository by
+ collectLastValue(
+ kosmos.fakeStatusBarModeRepository.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ val requiresStatusBarVisibleInWindowController by
+ collectLastValue(
+ kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+
+ // Start with an ongoing call (which should set status bar required)
+ postOngoingCallNotification()
+
+ assertThat(isStatusBarRequiredForOngoingCall).isTrue()
+ assertThat(requiresStatusBarVisibleInRepository).isTrue()
+ assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+
+ // Swipe away the chip
+ underTest.onStatusBarSwiped()
+
+ // Verify status bar is no longer required
+ assertThat(requiresStatusBarVisibleInRepository).isFalse()
+ assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+ }
+
+ private fun postOngoingCallNotification() {
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ uid = UID,
+ )
+ )
+ }
+ .build()
+ }
+
companion object {
private const val UID = 885
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 038722cd9608..bf1fbad074cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -20,8 +20,6 @@ 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.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons.G
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
@@ -40,12 +38,15 @@ import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesF
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.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.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.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
@@ -255,59 +256,146 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun contentDescription_notInService_usesNoPhone() =
testScope.runTest {
- var latest: ContentDescription? = null
- val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.contentDescription)
repository.isInService.value = false
- assertThat((latest as ContentDescription.Resource).res)
- .isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
- job.cancel()
+ @Test
+ fun contentDescription_includesNetworkName() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+
+ repository.isInService.value = true
+ repository.networkName.value = NetworkNameModel.SubscriptionDerived("Test Network Name")
+ repository.numberOfLevels.value = 5
+ repository.setAllLevels(3)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS))
}
@Test
fun contentDescription_inService_usesLevel() =
testScope.runTest {
- var latest: ContentDescription? = null
- val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.contentDescription)
repository.setAllLevels(2)
- assertThat((latest as ContentDescription.Resource).res)
- .isEqualTo(PHONE_SIGNAL_STRENGTH[2])
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
repository.setAllLevels(0)
- assertThat((latest as ContentDescription.Resource).res)
- .isEqualTo(PHONE_SIGNAL_STRENGTH[0])
- job.cancel()
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
}
@Test
- fun contentDescription_nonInflated_invalidLevelIsNull() =
+ fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() =
testScope.runTest {
val latest by collectLastValue(underTest.contentDescription)
repository.inflateSignalStrength.value = false
repository.setAllLevels(-1)
- assertThat(latest).isNull()
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
repository.setAllLevels(100)
- assertThat(latest).isNull()
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
+
+ @Test
+ fun contentDescription_nonInflated_levelStrings() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+
+ repository.inflateSignalStrength.value = false
+ repository.setAllLevels(0)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+
+ repository.setAllLevels(1)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+
+ repository.setAllLevels(2)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+
+ repository.setAllLevels(3)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+
+ repository.setAllLevels(4)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
}
@Test
- fun contentDescription_inflated_invalidLevelIsNull() =
+ fun contentDescription_inflated_invalidLevelUsesNoSignalText() =
testScope.runTest {
val latest by collectLastValue(underTest.contentDescription)
repository.inflateSignalStrength.value = true
repository.numberOfLevels.value = 6
+
repository.setAllLevels(-2)
- assertThat(latest).isNull()
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
repository.setAllLevels(100)
- assertThat(latest).isNull()
+
+ 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)
+
+ repository.inflateSignalStrength.value = true
+ repository.numberOfLevels.value = 6
+
+ // Note that the _repo_ level is 1 lower than the reported level through the interactor
+
+ repository.setAllLevels(0)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+
+ repository.setAllLevels(1)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+
+ repository.setAllLevels(2)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+
+ repository.setAllLevels(3)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS))
+
+ repository.setAllLevels(4)
+
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
}
@Test
@@ -323,7 +411,10 @@ class MobileIconViewModelTest : SysuiTestCase() {
repository.setAllLevels(i)
when (i) {
-1,
- 5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+ 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)
@@ -344,7 +435,10 @@ class MobileIconViewModelTest : SysuiTestCase() {
repository.setAllLevels(i)
when (i) {
-2,
- 5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+ 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)
@@ -967,5 +1061,13 @@ class MobileIconViewModelTest : SysuiTestCase() {
companion object {
private const val SUB_1_ID = 1
+
+ // For convenience, just define these as constants
+ private val NO_SIGNAL = R.string.accessibility_no_signal
+ private val ONE_BAR = R.string.accessibility_one_bar
+ private val TWO_BARS = R.string.accessibility_two_bars
+ private val THREE_BARS = R.string.accessibility_three_bars
+ private val FOUR_BARS = R.string.accessibility_four_bars
+ private val FULL_BARS = R.string.accessibility_signal_full
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index c0a206afe64b..9ad23154c334 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -49,11 +49,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
private val testScope = TestScope(dispatcher)
- private val iconsInteractor =
- FakeMobileIconsInteractor(
- FakeMobileMappingsProxy(),
- mock(),
- )
+ private val iconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val repo = FakeDeviceBasedSatelliteRepository()
private val connectivityRepository = FakeConnectivityRepository()
@@ -515,7 +511,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
// GIVEN, 2 connection
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
- val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
// WHEN all connections are NOT OOS.
i1.isInService.value = true
@@ -547,7 +543,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
// GIVEN a condition that should return true (all conections OOS)
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
- val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
i1.isInService.value = true
i2.isInService.value = true
@@ -579,4 +575,40 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
// THEN the interactor returns true due to the wifi network being active
assertThat(latest).isTrue()
}
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun isAnyConnectionNtn_trueWhenAnyNtn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+ // GIVEN, 2 connection
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+ // WHEN at least one connection is using ntn
+ i1.isNonTerrestrial.value = true
+ i2.isNonTerrestrial.value = false
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun isAnyConnectionNtn_falseWhenNoNtn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+ // GIVEN, 2 connection
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+ // WHEN at no connection is using ntn
+ i1.isNonTerrestrial.value = false
+ i2.isNonTerrestrial.value = false
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 509aa7ad67fd..fe5b56a4e66d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -327,10 +327,11 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
// GIVEN satellite is allowed
repo.isSatelliteAllowedForCurrentLocation.value = true
- // GIVEN all icons are OOS
+ // GIVEN all icons are OOS and not ntn
val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
i1.isInService.value = false
i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
@@ -344,6 +345,29 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
}
@Test
+ fun icon_nullWhenConnected_mobileNtnConnectionExists() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN ntn connection exists
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isNonTerrestrial.value = true
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // GIVEN satellite reports that it is Connected
+ repo.connectionState.value = SatelliteConnectionState.On
+
+ // THEN icon is null because despite being connected, the mobile stack is reporting a
+ // nonTerrestrial network, and therefore will have its own icon
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun icon_satelliteIsProvisioned() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
index 9888574071e6..a2ca12c13a3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
@@ -30,6 +30,7 @@ class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder {
var listener: StatusBarVisibilityChangeListener? = null
override fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 7b04fc827d83..5c1141b94bc8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -67,8 +67,9 @@ import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationSt
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
+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.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
@@ -76,6 +77,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBar
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Before
@@ -518,7 +520,27 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
}
@Test
- fun isClockVisible_allowedByFlags_hunActive_notVisible() =
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun isClockVisible_allowedByFlags_hunPinnedByUser_visible() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.isClockVisible)
+ transitionKeyguardToGone()
+
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+ // there is an active HUN
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun isClockVisible_allowedByFlags_hunPinnedBySystem_notVisible() =
kosmos.runTest {
val latest by collectLastValue(underTest.isClockVisible)
transitionKeyguardToGone()
@@ -527,7 +549,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
// there is an active HUN
headsUpNotificationRepository.setNotifications(
- fakeHeadsUpRowRepository(isPinned = true)
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+ )
)
assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
@@ -541,10 +566,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
fakeDisableFlagsRepository.disableFlags.value =
DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
- // there is an active HUN
+ // there is an active HUN pinned by the system
headsUpNotificationRepository.setNotifications(
- fakeHeadsUpRowRepository(isPinned = true)
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+ )
)
+ assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
// hun goes away
headsUpNotificationRepository.setNotifications(listOf())
@@ -562,7 +591,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE)
// there is an active HUN
headsUpNotificationRepository.setNotifications(
- fakeHeadsUpRowRepository(isPinned = true)
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+ )
)
assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
@@ -898,10 +930,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
- // Cribbed from [HeadsUpNotificationInteractorTest.kt]
- private fun fakeHeadsUpRowRepository(key: String = "test key", isPinned: Boolean = false) =
- FakeHeadsUpRowRepository(key = key, isPinned = isPinned)
-
private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
ActiveNotificationsStore.Builder()
.apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
index 1b7b47f49af2..2eac39e25759 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
@@ -66,7 +66,7 @@ class CastDeviceTest : SysuiTestCase() {
doAnswer {
// See Utils.isHeadlessRemoteDisplayProvider
if ((it.arguments[0] as Intent).`package` == HEADLESS_REMOTE_PACKAGE) {
- emptyList()
+ emptyList<ResolveInfo>()
} else {
listOf(mock<ResolveInfo>())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 4e33a5958a1a..3d6882c3fdaf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -251,6 +251,22 @@ class ZenModeInteractorTest : SysuiTestCase() {
}
@Test
+ fun deactivateAllModes_updatesCorrectModes() =
+ testScope.runTest {
+ zenModeRepository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder().setName("Inactive").setActive(false).build(),
+ TestModeBuilder().setName("Active").setActive(true).build(),
+ )
+ )
+
+ underTest.deactivateAllModes()
+
+ assertThat(zenModeRepository.getModes().filter { it.isActive }).isEmpty()
+ }
+
+ @Test
fun activeModes_computesMainActiveMode() =
testScope.runTest {
val activeModes by collectLastValue(underTest.activeModes)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
new file mode 100644
index 000000000000..2ad1124d72d4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.touchpad.tutorial.ui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
+import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StateTransitionsTest : SysuiTestCase() {
+
+ companion object {
+ private const val START_MARKER = "startMarker"
+ private const val END_MARKER = "endMarker"
+ private const val SUCCESS_ANIMATION = 0
+ }
+
+ // needed to simulate caching last state as it's used to create new state
+ private var lastState: TutorialActionState = NotStarted
+
+ private fun GestureState.toTutorialActionState(): TutorialActionState {
+ val newState =
+ this.toGestureUiState(
+ progressStartMarker = START_MARKER,
+ progressEndMarker = END_MARKER,
+ successAnimation = SUCCESS_ANIMATION,
+ )
+ .toTutorialActionState(lastState)
+ lastState = newState
+ return lastState
+ }
+
+ @Test
+ fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() {
+ val happyPath =
+ listOf(
+ GestureState.NotStarted,
+ GestureState.InProgress(0f),
+ GestureState.InProgress(0.5f),
+ GestureState.InProgress(1f),
+ GestureState.Finished,
+ )
+
+ val resultingStates = mutableListOf<TutorialActionState>()
+ happyPath.forEach { resultingStates.add(it.toTutorialActionState()) }
+
+ assertThat(resultingStates)
+ .containsExactly(
+ NotStarted,
+ InProgress(0f, START_MARKER, END_MARKER),
+ InProgress(0.5f, START_MARKER, END_MARKER),
+ InProgress(1f, START_MARKER, END_MARKER),
+ Finished(SUCCESS_ANIMATION),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() {
+ val errorPath =
+ listOf(
+ GestureState.NotStarted,
+ GestureState.InProgress(0f),
+ GestureState.Error,
+ GestureState.InProgress(0.5f),
+ GestureState.InProgress(1f),
+ GestureState.Finished,
+ )
+
+ val resultingStates = mutableListOf<TutorialActionState>()
+ errorPath.forEach { resultingStates.add(it.toTutorialActionState()) }
+
+ assertThat(resultingStates)
+ .containsExactly(
+ NotStarted,
+ InProgress(0f, START_MARKER, END_MARKER),
+ Error,
+ InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
+ InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
+ Finished(SUCCESS_ANIMATION),
+ )
+ .inOrder()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
index d9d81692e2b6..2f3f75fdb6ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
@@ -22,7 +22,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.LEFT
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.RIGHT
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -45,24 +44,6 @@ class BackGestureRecognizerTest : SysuiTestCase() {
}
@Test
- fun triggersGestureFinishedForThreeFingerGestureRight() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = Finished)
- }
-
- @Test
- fun triggersGestureFinishedForThreeFingerGestureLeft() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = Finished)
- }
-
- @Test
- fun triggersGestureProgressForThreeFingerGestureStarted() {
- assertStateAfterEvents(
- events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
- expectedState = InProgress(),
- )
- }
-
- @Test
fun triggersProgressRelativeToDistanceWhenSwipingLeft() {
assertProgressWhileMovingFingers(
deltaX = -SWIPE_DISTANCE / 2,
@@ -86,13 +67,6 @@ class BackGestureRecognizerTest : SysuiTestCase() {
)
}
- private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) {
- assertStateAfterEvents(
- events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
- expectedState = expected,
- )
- }
-
@Test
fun triggeredProgressIsNoBiggerThanOne() {
assertProgressWhileMovingFingers(
@@ -105,30 +79,13 @@ class BackGestureRecognizerTest : SysuiTestCase() {
)
}
- @Test
- fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+ private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) {
assertStateAfterEvents(
- events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
- expectedState = NotStarted,
+ events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+ expectedState = expected,
)
}
- @Test
- fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
- assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
- assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onFourFingersSwipe() {
- assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NotStarted)
- }
-
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
events.forEach { gestureRecognizer.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
index 7aa389a1739f..e23348be1590 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -57,22 +57,9 @@ class HomeGestureRecognizerTest : SysuiTestCase() {
}
@Test
- fun triggersGestureFinishedForThreeFingerGestureUp() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() {
+ fun triggersError_onGestureSpeedTooSlow() {
velocityTracker.setVelocity(Velocity(SLOW))
- assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
- @Test
- fun triggersGestureProgressForThreeFingerGestureStarted() {
- assertStateAfterEvents(
- events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
- expectedState = InProgress(),
- )
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Error)
}
@Test
@@ -81,13 +68,6 @@ class HomeGestureRecognizerTest : SysuiTestCase() {
assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
}
- private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
- assertStateAfterEvents(
- events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
- expectedState = InProgress(progress = expectedProgress),
- )
- }
-
@Test
fun triggeredProgressIsBetweenZeroAndOne() {
// going in the wrong direction
@@ -96,31 +76,13 @@ class HomeGestureRecognizerTest : SysuiTestCase() {
assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
}
- @Test
- fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+ private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
assertStateAfterEvents(
- events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
- expectedState = NotStarted,
+ events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+ expectedState = InProgress(progress = expectedProgress),
)
}
- @Test
- fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
- assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NotStarted)
- assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
- assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onFourFingersSwipe() {
- assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
events.forEach { gestureRecognizer.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
index cb74e6569b3f..2fe37aef7e0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -58,22 +58,9 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() {
}
@Test
- fun triggersGestureFinishedForThreeFingerGestureUp() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
+ fun triggersError_onGestureSpeedTooHigh() {
velocityTracker.setVelocity(Velocity(FAST))
- assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
- @Test
- fun triggersGestureProgressForThreeFingerGestureStarted() {
- assertStateAfterEvents(
- events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
- expectedState = InProgress(progress = 0f),
- )
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Error)
}
@Test
@@ -97,31 +84,6 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() {
assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
}
- @Test
- fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
- assertStateAfterEvents(
- events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
- expectedState = NotStarted,
- )
- }
-
- @Test
- fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
- assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
- assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NotStarted)
- assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
- assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
- @Test
- fun doesntTriggerGestureFinished_onFourFingersSwipe() {
- assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NotStarted)
- }
-
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
events.forEach { gestureRecognizer.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
new file mode 100644
index 000000000000..533665eaa626
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.FAST
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.SLOW
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.THRESHOLD_VELOCITY_PX_PER_MS
+import com.android.systemui.touchpad.ui.gesture.FakeVelocityTracker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class ThreeFingerGestureRecognizerTest(
+ private val recognizer: GestureRecognizer,
+ private val validGestures: Set<List<MotionEvent>>,
+ private val tooShortGesture: List<MotionEvent>,
+ @Suppress("UNUSED_PARAMETER") testSuffix: String, // here just for nicer test names
+) : SysuiTestCase() {
+
+ private var gestureState: GestureState = GestureState.NotStarted
+
+ @Before
+ fun before() {
+ recognizer.addGestureStateCallback { gestureState = it }
+ }
+
+ @Test
+ fun triggersGestureFinishedForValidGestures() {
+ validGestures.forEach { assertStateAfterEvents(events = it, expectedState = Finished) }
+ }
+
+ @Test
+ fun triggersGestureProgressForThreeFingerGestureStarted() {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
+ expectedState = InProgress(progress = 0f),
+ )
+ }
+
+ @Test
+ fun triggersGestureError_onGestureDistanceTooShort() {
+ assertStateAfterEvents(events = tooShortGesture, expectedState = Error)
+ }
+
+ @Test
+ fun triggersGestureError_onThreeFingersSwipeInOtherDirections() {
+ val allThreeFingerGestures =
+ listOf(
+ ThreeFingerGesture.swipeUp(),
+ ThreeFingerGesture.swipeDown(),
+ ThreeFingerGesture.swipeLeft(),
+ ThreeFingerGesture.swipeRight(),
+ )
+ val invalidGestures = allThreeFingerGestures.filter { it.differentFromAnyOf(validGestures) }
+ invalidGestures.forEach { assertStateAfterEvents(events = it, expectedState = Error) }
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
+ assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NotStarted)
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onFourFingersSwipe() {
+ assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NotStarted)
+ }
+
+ private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
+ events.forEach { recognizer.accept(it) }
+ assertThat(gestureState).isEqualTo(expectedState)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{3}")
+ fun gesturesToTest(): List<Array<Any>> =
+ with(ThreeFingerGesture) {
+ listOf(
+ GestureTestData(
+ recognizer = BackGestureRecognizer(SWIPE_DISTANCE.toInt()),
+ validGestures = setOf(swipeRight(), swipeLeft()),
+ tooShortGesture = swipeRight(SWIPE_DISTANCE / 2),
+ testSuffix = "back gesture",
+ ),
+ GestureTestData(
+ recognizer =
+ HomeGestureRecognizer(
+ SWIPE_DISTANCE.toInt(),
+ THRESHOLD_VELOCITY_PX_PER_MS,
+ FakeVelocityTracker(velocity = FAST),
+ ),
+ validGestures = setOf(swipeUp()),
+ tooShortGesture = swipeUp(SWIPE_DISTANCE / 2),
+ testSuffix = "home gesture",
+ ),
+ GestureTestData(
+ recognizer =
+ RecentAppsGestureRecognizer(
+ SWIPE_DISTANCE.toInt(),
+ THRESHOLD_VELOCITY_PX_PER_MS,
+ FakeVelocityTracker(velocity = SLOW),
+ ),
+ validGestures = setOf(swipeUp()),
+ tooShortGesture = swipeUp(SWIPE_DISTANCE / 2),
+ testSuffix = "recent apps gesture",
+ ),
+ )
+ .map {
+ arrayOf(it.recognizer, it.validGestures, it.tooShortGesture, it.testSuffix)
+ }
+ }
+ }
+
+ class GestureTestData(
+ val recognizer: GestureRecognizer,
+ val validGestures: Set<List<MotionEvent>>,
+ val tooShortGesture: List<MotionEvent>,
+ val testSuffix: String,
+ )
+}
+
+private fun List<MotionEvent>.differentFromAnyOf(validGestures: Set<List<MotionEvent>>): Boolean {
+ // comparing MotionEvents is really hard so let's just compare their positions
+ val positions = this.map { it.x to it.y }
+ val validGesturesPositions = validGestures.map { gesture -> gesture.map { it.x to it.y } }
+ return !validGesturesPositions.contains(positions)
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
index 3dcb82811408..72527ddf65b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -105,6 +105,20 @@ class ViewUtilTest : SysuiTestCase() {
assertThat(outRect.top).isEqualTo(VIEW_TOP)
assertThat(outRect.bottom).isEqualTo(VIEW_BOTTOM)
}
+
+ @Test
+ fun viewBoundsOnScreen_viewAnchoredAtOriginInWindow() {
+ // view is anchored at 0,0 in its window
+ view.setLeftTopRightBottom(0, 0, VIEW_RIGHT - VIEW_LEFT, VIEW_BOTTOM - VIEW_TOP)
+
+ val outRect = Rect()
+ view.viewBoundsOnScreen(outRect)
+
+ assertThat(outRect.left).isEqualTo(VIEW_LEFT)
+ assertThat(outRect.right).isEqualTo(VIEW_RIGHT)
+ assertThat(outRect.top).isEqualTo(VIEW_TOP)
+ assertThat(outRect.bottom).isEqualTo(VIEW_BOTTOM)
+ }
}
private const val VIEW_LEFT = 30
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index dcb15a7cd9aa..b2083c2921c9 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -213,6 +213,13 @@ public interface BcSmartspaceDataPlugin extends Plugin {
default int getCurrentCardTopPadding() {
throw new UnsupportedOperationException("Not implemented by " + getClass());
}
+
+ /**
+ * Set the horizontal paddings for applicable children inside the SmartspaceView.
+ */
+ default void setHorizontalPaddings(int horizontalPadding) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
}
/** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
index fb5ef02aa06a..843afb8d74a8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
@@ -115,14 +115,25 @@ class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
// and we're not planning to add this vide in clockHostView
// so we only need position of device entry icon to constrain clock
// Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
- val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
- val defaultDensity =
- DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
- DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
- val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
-
- connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin)
+ clockPreviewConfig.lockId?.let { lockId ->
+ connect(lockscreenClockViewLargeId, BOTTOM, lockId, TOP)
+ }
+ ?: run {
+ val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+ val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
+ connect(
+ lockscreenClockViewLargeId,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ clockBottomMargin,
+ )
+ }
+
val smallClockViewId = getId(context, "lockscreen_clock_view")
constrainWidth(smallClockViewId, WRAP_CONTENT)
constrainHeight(smallClockViewId, getDimen(context, "small_clock_height"))
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
index 544b705c55c5..94e669fc0ea2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
@@ -22,4 +22,5 @@ data class ClockPreviewConfig(
val previewContext: Context,
val isShadeLayoutWide: Boolean,
val isSceneContainerFlagEnabled: Boolean = false,
+ val lockId: Int? = null,
)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index e3cbd6643e5f..322da322ff0c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,7 +14,6 @@
package com.android.systemui.plugins.qs;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -22,6 +21,7 @@ import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
@@ -92,6 +92,7 @@ public interface QSTile {
CharSequence getTileLabel();
+ @NonNull
State getState();
default LogMaker populate(LogMaker logMaker) {
@@ -269,6 +270,7 @@ public interface QSTile {
return sb.append(']');
}
+ @NonNull
public State copy() {
State state = new State();
copyTo(state);
@@ -304,6 +306,7 @@ public interface QSTile {
return rt;
}
+ @androidx.annotation.NonNull
@Override
public State copy() {
AdapterState state = new AdapterState();
@@ -316,6 +319,7 @@ public interface QSTile {
class BooleanState extends AdapterState {
public static final int VERSION = 1;
+ @androidx.annotation.NonNull
@Override
public State copy() {
BooleanState state = new BooleanState();
diff --git a/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
index 47641ee64ee1..4a14f3baaafc 100644
--- a/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
+++ b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorSecondaryFixedDim"/>
+ <item android:color="@androidprv:color/materialColorSecondaryFixedDim"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
index 5e7cf3ee41fb..6a885a733dba 100644
--- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -19,7 +19,7 @@
android:color="?attr/wallpaperTextColorSecondary">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorTertiaryFixed"/>
+ <solid android:color="@androidprv:color/materialColorTertiaryFixed"/>
<corners android:radius="24dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res-keyguard/drawable/progress_bar.xml b/packages/SystemUI/res-keyguard/drawable/progress_bar.xml
index 910a74ad5faf..455d9d3c00fa 100644
--- a/packages/SystemUI/res-keyguard/drawable/progress_bar.xml
+++ b/packages/SystemUI/res-keyguard/drawable/progress_bar.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent"
android:shape="rectangle">
<corners android:radius="30dp" />
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
</shape>
</item>
<item
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
index 83736e9d9473..eb9ee03a2cd4 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
@@ -27,8 +27,8 @@
android:background="@drawable/kg_bouncer_secondary_button"
android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_no_sim"
- android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ android:drawableTint="@androidprv:color/materialColorOnSurface"
android:text="@string/disable_carrier_button_text"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="@dimen/kg_status_line_font_size"
android:visibility="gone" />
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index ba0d7de1d481..bfb37a0d97a7 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -110,6 +110,7 @@
<dimen name="below_clock_padding_start">32dp</dimen>
<dimen name="below_clock_padding_end">16dp</dimen>
<dimen name="below_clock_padding_start_icons">28dp</dimen>
+ <dimen name="smartspace_padding_horizontal">16dp</dimen>
<!-- Proportion of the screen height to use to set the maximum height of the bouncer to when
the device is in the DEVICE_POSTURE_HALF_OPENED posture, for the PIN/pattern entry. 0 will
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index da12dd731c23..b256518e99ac 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,13 +23,13 @@
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="Keyguard.Bouncer.PrimaryMessage" parent="Theme.SystemUI">
<item name="android:textSize">18sp</item>
<item name="android:lineHeight">24dp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:singleLine">true</item>
<item name="android:textAlignment">center</item>
<item name="android:ellipsize">marquee</item>
@@ -41,10 +41,10 @@
<item name="android:textAlignment">center</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:ellipsize">end</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
- <item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnTertiaryFixed</item>
<item name="android:textSize">16sp</item>
<item name="android:background">@drawable/kg_emergency_button_background</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
diff --git a/packages/SystemUI/res-product/values/strings.xml b/packages/SystemUI/res-product/values/strings.xml
index 42733a20ffae..0c29bb4b2279 100644
--- a/packages/SystemUI/res-product/values/strings.xml
+++ b/packages/SystemUI/res-product/values/strings.xml
@@ -179,6 +179,8 @@
<!-- Text informing the user that their media is now playing on this tablet device. [CHAR LIMIT=50] -->
<string name="media_transfer_playing_this_device" product="tablet">Playing on this tablet</string>
-
+ <!-- Message shown during shutdown when Find My Device with Dead Battery Finder is active [CHAR LIMIT=300] -->
+ <string name="finder_active" product="default">You can locate this phone with Find My Device even when powered off</string>
+ <string name="finder_active" product="tablet">You can locate this tablet with Find My Device even when powered off</string>
</resources>
diff --git a/packages/SystemUI/res/color/connected_network_primary_color.xml b/packages/SystemUI/res/color/connected_network_primary_color.xml
index f173c8dd5473..920047a705e8 100644
--- a/packages/SystemUI/res/color/connected_network_primary_color.xml
+++ b/packages/SystemUI/res/color/connected_network_primary_color.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorOnPrimaryContainer" />
+ <item android:color="@androidprv:color/materialColorOnPrimaryContainer" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/disconnected_network_primary_color.xml b/packages/SystemUI/res/color/disconnected_network_primary_color.xml
index 536bf78b7b60..f4b19a71fefd 100644
--- a/packages/SystemUI/res/color/disconnected_network_primary_color.xml
+++ b/packages/SystemUI/res/color/disconnected_network_primary_color.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorPrimaryContainer" />
+ <item android:color="@androidprv:color/materialColorPrimaryContainer" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/menu_item_text.xml b/packages/SystemUI/res/color/menu_item_text.xml
index 0d05650b8082..c1c9e2c1938e 100644
--- a/packages/SystemUI/res/color/menu_item_text.xml
+++ b/packages/SystemUI/res/color/menu_item_text.xml
@@ -17,8 +17,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorOnSurface"
+ android:color="@androidprv:color/materialColorOnSurface"
android:alpha="0.38" />
- <item android:color="?androidprv:attr/materialColorOnSurface"/>
+ <item android:color="@androidprv:color/materialColorOnSurface"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/notification_focus_overlay_color.xml b/packages/SystemUI/res/color/notification_focus_overlay_color.xml
index 6a3c7a148963..55843635dee7 100644
--- a/packages/SystemUI/res/color/notification_focus_overlay_color.xml
+++ b/packages/SystemUI/res/color/notification_focus_overlay_color.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_focused="true" android:color="?androidprv:attr/materialColorSecondary" />
+ <item android:state_focused="true" android:color="@androidprv:color/materialColorSecondary" />
<item android:color="@color/transparent" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
index d1b8a064724d..e0873b887d8d 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true"
- android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
+ android:color="@androidprv:color/materialColorOnSurfaceVariant" />
<item android:color="@color/notification_guts_priority_button_bg_stroke_color" />
</selector>
diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
index cc8c25a2d1ec..3b221e76a91d 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true"
- android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
+ android:color="@androidprv:color/materialColorOnSurfaceVariant" />
<item android:color="@color/notification_guts_priority_button_content_color" />
</selector>
diff --git a/packages/SystemUI/res/color/notification_state_color_default.xml b/packages/SystemUI/res/color/notification_state_color_default.xml
index a14a7ad9d2da..9d77f604c166 100644
--- a/packages/SystemUI/res/color/notification_state_color_default.xml
+++ b/packages/SystemUI/res/color/notification_state_color_default.xml
@@ -19,7 +19,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- Pressed state's alpha is set to 0.00 temporarily until this bug is resolved permanently
b/313920497 Design intended alpha is 0.15-->
- <item android:state_pressed="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.00" />
- <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.11" />
+ <item android:state_pressed="true" android:color="@androidprv:color/materialColorOnSurface" android:alpha="0.00" />
+ <item android:state_hovered="true" android:color="@androidprv:color/materialColorOnSurface" android:alpha="0.11" />
<item android:color="@color/transparent" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_filled_background.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_background.xml
index 40bab5ed08f2..898d5891b7e2 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_filled_background.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_background.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorPrimary"
+ android:color="@androidprv:color/materialColorPrimary"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorPrimary"/>
+ <item android:color="@androidprv:color/materialColorPrimary"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
index f8d4af57229b..c8ab4ad88ca1 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorPrimaryFixed"
+ android:color="@androidprv:color/materialColorPrimaryFixed"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorPrimaryFixed"/>
+ <item android:color="@androidprv:color/materialColorPrimaryFixed"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
index faba8fc4c755..60b6245ca301 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+ android:color="@androidprv:color/materialColorOnPrimaryFixed"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorOnPrimaryFixed"/>
+ <item android:color="@androidprv:color/materialColorOnPrimaryFixed"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_filled_text_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_text_color.xml
index e76ad991a92c..a5497a54ec66 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_filled_text_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_text_color.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorOnPrimary"
+ android:color="@androidprv:color/materialColorOnPrimary"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorOnPrimary"/>
+ <item android:color="@androidprv:color/materialColorOnPrimary"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_outline.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
index 1adfe5b19d70..7ef7e062e579 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorPrimary"
+ android:color="@androidprv:color/materialColorPrimary"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorPrimary"/>
+ <item android:color="@androidprv:color/materialColorPrimary"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
index 5dc994f23f2b..f139008640ad 100644
--- a/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_enabled="false"
- android:color="?androidprv:attr/materialColorOnSurface"
+ android:color="@androidprv:color/materialColorOnSurface"
android:alpha="0.30"/>
- <item android:color="?androidprv:attr/materialColorOnSurface"/>
+ <item android:color="@androidprv:color/materialColorOnSurface"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_hint.xml b/packages/SystemUI/res/color/remote_input_hint.xml
index 0d90ee6b47c6..75d2bb8f2dfe 100644
--- a/packages/SystemUI/res/color/remote_input_hint.xml
+++ b/packages/SystemUI/res/color/remote_input_hint.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorOnSurfaceVariant" android:alpha=".6" />
+ <item android:color="@androidprv:color/materialColorOnSurfaceVariant" android:alpha=".6" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
index 0acc66b9050f..4c61c0c69ca1 100644
--- a/packages/SystemUI/res/color/remote_input_send.xml
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_enabled="false" android:color="?androidprv:attr/materialColorPrimary" android:alpha=".3" />
- <item android:color="?androidprv:attr/materialColorPrimary" />
+ <item android:state_enabled="false" android:color="@androidprv:color/materialColorPrimary" android:alpha=".3" />
+ <item android:color="@androidprv:color/materialColorPrimary" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
index bf2c198fe540..2ec09cde26ca 100644
--- a/packages/SystemUI/res/color/remote_input_text.xml
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_enabled="false" android:color="?androidprv:attr/materialColorOnSurfaceVariant" android:alpha=".6" />
- <item android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
+ <item android:state_enabled="false" android:color="@androidprv:color/materialColorOnSurfaceVariant" android:alpha=".6" />
+ <item android:color="@androidprv:color/materialColorOnSurfaceVariant" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/screenshare_options_spinner_background.xml b/packages/SystemUI/res/color/screenshare_options_spinner_background.xml
index 922813dcbe64..f3059f6e4627 100644
--- a/packages/SystemUI/res/color/screenshare_options_spinner_background.xml
+++ b/packages/SystemUI/res/color/screenshare_options_spinner_background.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_hovered="false" android:state_focused="false" android:color="@android:color/transparent" />
- <item android:state_focused="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha=".1" />
- <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha=".08" />
+ <item android:state_focused="true" android:color="@androidprv:color/materialColorOnSurface" android:alpha=".1" />
+ <item android:state_hovered="true" android:color="@androidprv:color/materialColorOnSurface" android:alpha=".08" />
</selector>
diff --git a/packages/SystemUI/res/color/slider_active_track_color.xml b/packages/SystemUI/res/color/slider_active_track_color.xml
index a5aa58dd6b51..8ba5e4901a7a 100644
--- a/packages/SystemUI/res/color/slider_active_track_color.xml
+++ b/packages/SystemUI/res/color/slider_active_track_color.xml
@@ -14,6 +14,6 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorPrimary" android:state_enabled="true" />
- <item android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
+ <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_inactive_track_color.xml b/packages/SystemUI/res/color/slider_inactive_track_color.xml
index 89aef776c00e..7980f804a516 100644
--- a/packages/SystemUI/res/color/slider_inactive_track_color.xml
+++ b/packages/SystemUI/res/color/slider_inactive_track_color.xml
@@ -14,6 +14,6 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorSurfaceContainerHighest" android:state_enabled="true" />
- <item android:color="?androidprv:attr/materialColorPrimary" />
+ <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="true" />
+ <item android:color="@androidprv:color/materialColorPrimary" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_thumb_color.xml b/packages/SystemUI/res/color/slider_thumb_color.xml
index 5206049edd41..8a98902426f8 100644
--- a/packages/SystemUI/res/color/slider_thumb_color.xml
+++ b/packages/SystemUI/res/color/slider_thumb_color.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/materialColorSurfaceContainerHighest" android:state_enabled="false" />
- <item android:color="?androidprv:attr/materialColorPrimary" />
+ <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="false" />
+ <item android:color="@androidprv:color/materialColorPrimary" />
</selector>
diff --git a/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_down.xml b/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_down.xml
index 16076b17a6e5..d54164be4632 100644
--- a/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_down.xml
+++ b/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_down.xml
@@ -24,8 +24,8 @@
android:viewportHeight="24">
<path
android:pathData="M0,12C0,5.373 5.373,0 12,0C18.627,0 24,5.373 24,12C24,18.627 18.627,24 12,24C5.373,24 0,18.627 0,12Z"
- android:fillColor="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ android:fillColor="@androidprv:color/materialColorSurfaceContainerHigh"/>
<path
android:pathData="M7.607,9.059L6.667,9.999L12,15.332L17.333,9.999L16.393,9.059L12,13.445"
- android:fillColor="?androidprv:attr/materialColorOnSurface"/>
+ android:fillColor="@androidprv:color/materialColorOnSurface"/>
</vector>
diff --git a/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_up.xml b/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_up.xml
index 309770ddd76d..81184a111d25 100644
--- a/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_up.xml
+++ b/packages/SystemUI/res/drawable-night/privacy_dialog_expand_toggle_up.xml
@@ -24,8 +24,8 @@
android:viewportHeight="24">
<path
android:pathData="M0,12C0,5.3726 5.3726,0 12,0C18.6274,0 24,5.3726 24,12C24,18.6274 18.6274,24 12,24C5.3726,24 0,18.6274 0,12Z"
- android:fillColor="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ android:fillColor="@androidprv:color/materialColorSurfaceContainerHigh"/>
<path
android:pathData="M16.3934,14.9393L17.3334,13.9993L12.0001,8.666L6.6667,13.9993L7.6068,14.9393L12.0001,10.5527"
- android:fillColor="?androidprv:attr/materialColorOnSurface"/>
+ android:fillColor="@androidprv:color/materialColorOnSurface"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/action_chip_background.xml b/packages/SystemUI/res/drawable/action_chip_background.xml
index 9492472a2be1..0958c840994d 100644
--- a/packages/SystemUI/res/drawable/action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_background.xml
@@ -20,7 +20,7 @@
android:color="@color/overlay_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSecondary"/>
+ <solid android:color="@androidprv:color/materialColorSecondary"/>
<corners android:radius="@dimen/overlay_button_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/action_chip_container_background.xml b/packages/SystemUI/res/drawable/action_chip_container_background.xml
index 2ee27107d900..5aced9d424fa 100644
--- a/packages/SystemUI/res/drawable/action_chip_container_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_container_background.xml
@@ -18,6 +18,6 @@
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:attr/materialColorSurfaceBright"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceBright"/>
<corners android:radius="@dimen/overlay_action_container_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/biometric_prompt_vertical_list_content_view_background.xml b/packages/SystemUI/res/drawable/biometric_prompt_vertical_list_content_view_background.xml
index fdafe6d8e335..63d026842234 100644
--- a/packages/SystemUI/res/drawable/biometric_prompt_vertical_list_content_view_background.xml
+++ b/packages/SystemUI/res/drawable/biometric_prompt_vertical_list_content_view_background.xml
@@ -18,6 +18,6 @@
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:attr/materialColorSurfaceContainerHigh"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh"/>
<corners android:radius="@dimen/biometric_prompt_content_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/brightness_bar.xml b/packages/SystemUI/res/drawable/brightness_bar.xml
index 3d1c1fbd6ce7..a32496b25041 100644
--- a/packages/SystemUI/res/drawable/brightness_bar.xml
+++ b/packages/SystemUI/res/drawable/brightness_bar.xml
@@ -21,7 +21,7 @@
android:viewportHeight="48">
<path
android:pathData="M2,22L302,22A2,2 0,0 1,304 24L304,24A2,2 0,0 1,302 26L2,26A2,2 0,0 1,0 24L0,24A2,2 0,0 1,2 22z"
- android:fillColor="?androidprv:attr/customColorShadeInactive"/>
+ android:fillColor="@androidprv:color/customColorShadeInactive"/>
<path
android:pathData="M24,0L205.71,0A24,24 0,0 1,229.71 24L229.71,24A24,24 0,0 1,205.71 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
android:fillColor="?attr/shadeActive"/>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index ec15b10851c5..88d3ecb3f423 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:attr/customColorShadeInactive" />
+ <solid android:color="@androidprv:color/customColorShadeInactive" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml b/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml
index 22406ec52d00..546289837e8f 100644
--- a/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml
+++ b/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml
@@ -22,7 +22,7 @@
<inset android:inset="-5dp">
<shape>
<corners android:radius="16dp"/>
- <stroke android:width="3dp" android:color="?androidprv:attr/materialColorSecondaryFixed"/>
+ <stroke android:width="3dp" android:color="@androidprv:color/materialColorSecondaryFixed"/>
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/chipbar_background.xml b/packages/SystemUI/res/drawable/chipbar_background.xml
index 7530f5ba244a..15c9fcb1370a 100644
--- a/packages/SystemUI/res/drawable/chipbar_background.xml
+++ b/packages/SystemUI/res/drawable/chipbar_background.xml
@@ -17,6 +17,6 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/materialColorSecondaryFixed" />
+ <solid android:color="@androidprv:color/materialColorSecondaryFixed" />
<corners android:radius="32dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
index a3832eef957f..fcf5b30f6211 100644
--- a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
+++ b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
@@ -20,7 +20,7 @@
android:color="?android:textColorPrimary">
<item android:id="@android:id/background">
<shape>
- <solid android:color="?androidprv:attr/materialColorPrimaryFixedDim"/>
+ <solid android:color="@androidprv:color/materialColorPrimaryFixedDim"/>
<corners android:radius="24dp" />
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml b/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml
index d7000d7f5a5c..b8911637f300 100644
--- a/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml
@@ -19,6 +19,6 @@
android:insetBottom="@dimen/contextual_edu_dialog_elevation">
<shape>
<corners android:radius="28dp" />
- <solid android:color="?androidprv:attr/materialColorTertiaryFixed" />
+ <solid android:color="@androidprv:color/materialColorTertiaryFixed" />
</shape>
</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/hearing_devices_spinner_popup_background.xml b/packages/SystemUI/res/drawable/hearing_devices_spinner_popup_background.xml
index f35975ee8548..5ddc860f85a2 100644
--- a/packages/SystemUI/res/drawable/hearing_devices_spinner_popup_background.xml
+++ b/packages/SystemUI/res/drawable/hearing_devices_spinner_popup_background.xml
@@ -18,5 +18,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_button_dark.xml b/packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml
index f3800e05148e..0640116a4e97 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_immersive_button_dark.xml
+++ b/packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2024 The Android Open Source Project
~
@@ -15,11 +14,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
<path
android:fillColor="#000000"
- android:pathData="M5,5H10V7H7V10H5V5M14,5H19V10H17V7H14V5M17,14H19V19H14V17H17V14M10,17V19H5V14H7V17H10Z"/>
+ android:pathData="M480,616 L240,376l56,-56 184,184 184,-184 56,56 -240,240Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml b/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml
new file mode 100644
index 000000000000..65a3eef4bdf1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M480,432 L296,616l-56,-56 240,-240 240,240 -56,56 -184,-184Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_check_circle_filled_24dp.xml b/packages/SystemUI/res/drawable/ic_check_circle_filled_24dp.xml
index 16e2a3db2e95..307f7eb8f17e 100644
--- a/packages/SystemUI/res/drawable/ic_check_circle_filled_24dp.xml
+++ b/packages/SystemUI/res/drawable/ic_check_circle_filled_24dp.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="24dp"
android:height="24dp"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:viewportHeight="24"
android:viewportWidth="24">
<path
diff --git a/packages/SystemUI/res/drawable/ic_circle_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_circle_outline_24dp.xml
index 82fa4f08d0c8..8304fd5be16e 100644
--- a/packages/SystemUI/res/drawable/ic_circle_outline_24dp.xml
+++ b/packages/SystemUI/res/drawable/ic_circle_outline_24dp.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="24dp"
android:height="24dp"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:viewportHeight="24"
android:viewportWidth="24">
<path
diff --git a/packages/SystemUI/res/drawable/ic_screensaver_auto.xml b/packages/SystemUI/res/drawable/ic_screensaver_auto.xml
new file mode 100644
index 000000000000..7cccff624c6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screensaver_auto.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<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="M160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L160,720Q160,720 160,720Q160,720 160,720L160,240Q160,240 160,240Q160,240 160,240L160,240Q160,240 160,240Q160,240 160,240Q160,240 160,240Q160,240 160,240ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L440,160Q440,179 440,199Q440,219 440,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L800,720Q800,720 800,720Q800,720 800,720L800,480Q823,480 843,480Q863,480 880,480L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM700,480Q700,388 636,324Q572,260 480,260Q572,260 636,196Q700,132 700,40Q700,132 764,196Q828,260 920,260Q828,260 764,324Q700,388 700,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
index 0406f0e4304e..ddff88484796 100644
--- a/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
+++ b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
- android:tint="?androidprv:attr/materialColorOnSurfaceVariant">
+ android:tint="@androidprv:color/materialColorOnSurfaceVariant">
<path
android:fillColor="@android:color/white"
android:strokeColor="@android:color/white"
diff --git a/packages/SystemUI/res/drawable/immersive_cling_bg.xml b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
index de29c32390e1..b28a423ea06b 100644
--- a/packages/SystemUI/res/drawable/immersive_cling_bg.xml
+++ b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
@@ -20,5 +20,5 @@
<corners
android:bottomLeftRadius="28dp"
android:bottomRightRadius="28dp"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 32dc4b335f7e..6ad46d837acd 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -20,7 +20,7 @@
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:attr/materialColorSurfaceContainerHigh"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh"/>
<size
android:width="@dimen/keyguard_affordance_fixed_width"
android:height="@dimen/keyguard_affordance_fixed_height"/>
diff --git a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
index fe76ba7e5b8c..fab0cc204e87 100644
--- a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
+++ b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
@@ -26,7 +26,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSecondaryFixed" />
+ <solid android:color="@androidprv:color/materialColorSecondaryFixed" />
<corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index b9597375c3df..1d5e09d9b260 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -26,7 +26,7 @@
<padding
android:left="20dp"
android:right="20dp" />
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml
index 84e2231738d4..200976b1fb31 100644
--- a/packages/SystemUI/res/drawable/notification_guts_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml
@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
<!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners -->
<corners android:radius="1dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 715be074eaa8..db3b969cca39 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -20,7 +20,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
</shape>
</item>
<item>
diff --git a/packages/SystemUI/res/drawable/overlay_border.xml b/packages/SystemUI/res/drawable/overlay_border.xml
index a59f9239dfca..381849be5730 100644
--- a/packages/SystemUI/res/drawable/overlay_border.xml
+++ b/packages/SystemUI/res/drawable/overlay_border.xml
@@ -18,6 +18,6 @@
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:attr/materialColorSurfaceBright"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceBright"/>
<corners android:radius="16dp"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_down.xml b/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_down.xml
index f8b99f4a0ee4..299b4c9ab1d0 100644
--- a/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_down.xml
+++ b/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_down.xml
@@ -23,8 +23,8 @@
android:viewportHeight="24">
<path
android:pathData="M0,12C0,5.373 5.373,0 12,0C18.627,0 24,5.373 24,12C24,18.627 18.627,24 12,24C5.373,24 0,18.627 0,12Z"
- android:fillColor="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ android:fillColor="@androidprv:color/materialColorSurfaceContainerHigh"/>
<path
android:pathData="M7.607,9.059L6.667,9.999L12,15.332L17.333,9.999L16.393,9.059L12,13.445"
- android:fillColor="?androidprv:attr/materialColorOnSurface"/>
+ android:fillColor="@androidprv:color/materialColorOnSurface"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_up.xml b/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_up.xml
index ae60d517ceb4..68e73e941e00 100644
--- a/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_up.xml
+++ b/packages/SystemUI/res/drawable/privacy_dialog_expand_toggle_up.xml
@@ -23,8 +23,8 @@
android:viewportHeight="24">
<path
android:pathData="M0,12C0,5.3726 5.3726,0 12,0C18.6274,0 24,5.3726 24,12C24,18.6274 18.6274,24 12,24C5.3726,24 0,18.6274 0,12Z"
- android:fillColor="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ android:fillColor="@androidprv:color/materialColorSurfaceContainerHigh"/>
<path
android:pathData="M16.3934,14.9393L17.3334,13.9993L12.0001,8.666L6.6667,13.9993L7.6068,14.9393L12.0001,10.5527"
- android:fillColor="?androidprv:attr/materialColorOnSurface"/>
+ android:fillColor="@androidprv:color/materialColorOnSurface"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
index 3c1668405909..e90473bf54f3 100644
--- a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
+++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
@@ -21,7 +21,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer"/>
<corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_tile_focused_background.xml b/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
index 33f0d02efb2a..cdf6a1a00316 100644
--- a/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
@@ -21,6 +21,6 @@
<corners android:radius="30dp" />
<stroke
android:width="3dp"
- android:color="?androidprv:attr/materialColorSecondaryFixed" />
+ android:color="@androidprv:color/materialColorSecondaryFixed" />
</shape>
</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
index 45d1a530cd20..01ecbcf9956b 100644
--- a/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
+++ b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
@@ -17,10 +17,10 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceDim" />
+ <solid android:color="@androidprv:color/materialColorSurfaceDim" />
<stroke
android:width="@dimen/remote_input_view_text_stroke"
- android:color="?androidprv:attr/materialColorPrimary"/>
+ android:color="@androidprv:color/materialColorPrimary"/>
<padding
android:bottom="0dp"
android:left="12dp"
diff --git a/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml b/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml
index 321a04a1fb5e..323cb77eb37f 100644
--- a/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml
+++ b/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml
@@ -17,5 +17,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/screenrecord_spinner_background_radius"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_edit_background.xml b/packages/SystemUI/res/drawable/screenshot_edit_background.xml
index 07e5aff3954d..ef1c30f10605 100644
--- a/packages/SystemUI/res/drawable/screenshot_edit_background.xml
+++ b/packages/SystemUI/res/drawable/screenshot_edit_background.xml
@@ -20,7 +20,7 @@
android:color="@color/overlay_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSecondaryFixedDim"/>
+ <solid android:color="@androidprv:color/materialColorSecondaryFixedDim"/>
<corners android:radius="16dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
index fab2d8db859f..2063d9fa55e0 100644
--- a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -20,7 +20,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer"/>
<corners android:radius="@dimen/settingslib_switch_bar_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
index e316a93c1c3d..70a1e84e7e40 100644
--- a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -24,7 +24,7 @@
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="?androidprv:attr/materialColorOnPrimary"/>
+ <solid android:color="@androidprv:color/materialColorOnPrimary"/>
</shape>
</item>
</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
index e2e64684f9c3..e3476a42b2bc 100644
--- a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -22,6 +22,6 @@
android:height="@dimen/settingslib_switch_track_height">
<padding android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"/>
- <solid android:color="?androidprv:attr/materialColorPrimary"/>
+ <solid android:color="@androidprv:color/materialColorPrimary"/>
<corners android:radius="@dimen/settingslib_switch_track_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/shelf_action_chip_background.xml b/packages/SystemUI/res/drawable/shelf_action_chip_background.xml
index 63600beff126..faa3d6869a34 100644
--- a/packages/SystemUI/res/drawable/shelf_action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/shelf_action_chip_background.xml
@@ -20,7 +20,7 @@
android:color="@color/overlay_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSecondary"/>
+ <solid android:color="@androidprv:color/materialColorSecondary"/>
<corners android:radius="10000dp"/> <!-- fully-rounded radius -->
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml b/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
index ad6c154692ec..82f034bb1cdd 100644
--- a/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
+++ b/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
@@ -21,7 +21,7 @@
<shape
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceBright"/>
<corners android:radius="10000dp"/> <!-- fully-rounded radius -->
</shape>
</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_button_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
index 2e2d9b9a3e35..b6a14c8188ab 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
@@ -22,7 +22,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/ksh_button_corner_radius"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceBright"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
index 5b88bb922a9e..2b95a9462365 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
@@ -22,7 +22,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/ksh_button_corner_radius"/>
- <solid android:color="?androidprv:attr/materialColorPrimary"/>
+ <solid android:color="@androidprv:color/materialColorPrimary"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/shortcut_search_background.xml b/packages/SystemUI/res/drawable/shortcut_search_background.xml
index d6847f0abb8d..07e5b3dec059 100644
--- a/packages/SystemUI/res/drawable/shortcut_search_background.xml
+++ b/packages/SystemUI/res/drawable/shortcut_search_background.xml
@@ -19,7 +19,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
+ <solid android:color="@androidprv:color/materialColorSurfaceBright" />
<corners android:radius="@dimen/ksh_search_box_corner_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
index 2675906580f1..6390c11dae36 100644
--- a/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
+++ b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
@@ -20,7 +20,7 @@
<shape android:shape="oval">
<size android:width="24dp"
android:height="24dp" />
- <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
+ <solid android:color="@androidprv:color/materialColorSurfaceBright"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
index 06bed001ae1a..42a5d305fb65 100644
--- a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
+++ b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
@@ -21,6 +21,6 @@
android:viewportWidth="40"
android:viewportHeight="40">
<path
- android:fillColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:fillColor="@androidprv:color/materialColorOnSurfaceVariant"
android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7267L16.2734 24.6667L20 20.94L23.7267 24.6667L24.6667 23.7267L20.94 20L24.6667 16.2733Z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/volume_background_top.xml b/packages/SystemUI/res/drawable/volume_background_top.xml
index 132572a41a36..7185d03a9910 100644
--- a/packages/SystemUI/res/drawable/volume_background_top.xml
+++ b/packages/SystemUI/res/drawable/volume_background_top.xml
@@ -17,7 +17,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape>
- <solid android:color="?androidprv:attr/materialColorSurface" />
+ <solid android:color="@androidprv:color/materialColorSurface" />
<corners android:topLeftRadius="@dimen/volume_dialog_background_corner_radius"
android:topRightRadius="@dimen/volume_dialog_background_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index 7d7498feeba6..25d78e3474d5 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -18,5 +18,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/volume_dialog_background_corner_radius" />
- <solid android:color="?androidprv:attr/materialColorSurface" />
+ <solid android:color="@androidprv:color/materialColorSurface" />
</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml b/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml
index 7d794966c480..9026d64aa969 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml
@@ -17,5 +17,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/volume_dialog_background_square_corner_radius" />
- <solid android:color="?androidprv:attr/materialColorSurface" />
+ <solid android:color="@androidprv:color/materialColorSurface" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
index 131201e7a94f..555eebd77b1e 100644
--- a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -19,6 +19,6 @@
<size
android:height="@dimen/volume_dialog_ringer_drawer_button_size"
android:width="@dimen/volume_dialog_ringer_drawer_button_size" />
- <solid android:color="?androidprv:attr/materialColorPrimary" />
+ <solid android:color="@androidprv:color/materialColorPrimary" />
<corners android:radius="@dimen/volume_dialog_ringer_selected_button_background_radius" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
index a8c9818b58a3..4b3edb905dca 100644
--- a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
@@ -17,6 +17,6 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle" >
<size android:width="@dimen/volume_dialog_ringer_drawer_button_size" android:height="@dimen/volume_dialog_ringer_drawer_button_size"/>
- <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
<corners android:radius="@dimen/volume_dialog_background_square_corner_radius" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
index d7d75d4304d0..c47d7c0e8eb1 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -26,7 +26,7 @@
<shape>
<size android:height="@dimen/volume_dialog_track_width" />
<corners android:radius="@dimen/volume_dialog_panel_width_half" />
- <solid android:color="?androidprv:attr/materialColorOutlineVariant" />
+ <solid android:color="@androidprv:color/materialColorOutlineVariant" />
</shape>
</item>
<item android:id="@android:id/progress"
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
index ca7df86d8296..3e53bf45cff8 100644
--- a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -42,7 +42,7 @@
android:layout_marginBottom="16dp"
android:scaleType="fitCenter"
android:src="@null"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
/>
<TextView
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index 7b7c96cb0322..afc58cc8e163 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -31,10 +31,10 @@
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:background="@drawable/overlay_button_background"
- android:backgroundTint="?androidprv:attr/materialColorPrimary"
+ android:backgroundTint="@androidprv:color/materialColorPrimary"
android:paddingHorizontal="24dp"
android:text="@string/app_clips_save_add_to_note"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
app:layout_constraintBottom_toTopOf="@id/preview"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -61,7 +61,7 @@
android:button="@drawable/checkbox_circle_shape"
android:checked="true"
android:text="@string/backlinks_include_link"
- android:textColor="?androidprv:attr/materialColorOnBackground"
+ android:textColor="@androidprv:color/materialColorOnBackground"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/preview"
app:layout_constraintStart_toEndOf="@id/cancel"
@@ -89,11 +89,11 @@
android:layout_marginStart="8dp"
android:drawablePadding="4dp"
android:drawableStart="@drawable/ic_info_outline"
- android:drawableTint="?androidprv:attr/materialColorOnBackground"
+ android:drawableTint="@androidprv:color/materialColorOnBackground"
android:gravity="center"
android:paddingHorizontal="8dp"
android:text="@string/backlinks_cross_profile_error"
- android:textColor="?androidprv:attr/materialColorOnBackground"
+ android:textColor="@androidprv:color/materialColorOnBackground"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/preview"
app:layout_constraintStart_toEndOf="@id/backlinks_data"
diff --git a/packages/SystemUI/res/layout/bundle_notification_info.xml b/packages/SystemUI/res/layout/bundle_notification_info.xml
index 8700832c0eb0..745066a7bdbb 100644
--- a/packages/SystemUI/res/layout/bundle_notification_info.xml
+++ b/packages/SystemUI/res/layout/bundle_notification_info.xml
@@ -103,7 +103,7 @@ asked for it -->
android:contentDescription="@string/notification_app_settings"
android:src="@drawable/ic_info"
android:layout_toStartOf="@id/info"
- android:tint="?androidprv:attr/materialColorPrimary"/>
+ android:tint="@androidprv:color/materialColorPrimary"/>
<ImageButton
android:id="@+id/info"
android:layout_width="@dimen/notification_importance_toggle_size"
@@ -112,7 +112,7 @@ asked for it -->
android:contentDescription="@string/notification_more_settings"
android:background="@drawable/ripple_drawable_20dp"
android:src="@drawable/ic_settings"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:layout_alignParentEnd="true" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index e1b8ab469765..2fecf79e926e 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -56,10 +56,10 @@
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/Chipbar.Text"
- android:textColor="?androidprv:attr/materialColorOnSecondaryFixed"
+ android:textColor="@androidprv:color/materialColorOnSecondaryFixed"
android:alpha="0.0"
/>
- <!-- LINT.ThenChange(systemui.temporarydisplay.chipbar.ChipbarInfo.kt) -->
+ <!-- LINT.ThenChange(/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt) -->
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
<ImageView
@@ -68,7 +68,7 @@
android:layout_height="@dimen/chipbar_end_icon_size"
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
android:src="@drawable/ic_progress_activity"
- android:tint="?androidprv:attr/materialColorOnSecondaryFixedVariant"
+ android:tint="@androidprv:color/materialColorOnSecondaryFixedVariant"
android:alpha="0.0"
/>
@@ -88,7 +88,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
style="@style/Chipbar.Text"
- android:textColor="?androidprv:attr/materialColorOnPrimaryFixed"
+ android:textColor="@androidprv:color/materialColorOnPrimaryFixed"
android:paddingStart="@dimen/chipbar_outer_padding"
android:paddingEnd="@dimen/chipbar_outer_padding"
android:paddingTop="@dimen/chipbar_end_button_vertical_padding"
diff --git a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
index 14b3b55df0a4..3378dcced537 100644
--- a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
+++ b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
@@ -17,8 +17,8 @@
android:paddingHorizontal="16dp"
android:background="@drawable/overlay_button_background"
android:text="@string/clipboard_edit_text_done"
- android:backgroundTint="?androidprv:attr/materialColorPrimary"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:backgroundTint="@androidprv:color/materialColorPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 572f063c20c4..448b3e7d5ea0 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -222,8 +222,8 @@
android:layout_height="match_parent"
android:layout_margin="@dimen/overlay_dismiss_button_margin"
android:background="@drawable/circular_background"
- android:backgroundTint="?androidprv:attr/materialColorPrimaryFixedDim"
- android:tint="?androidprv:attr/materialColorOnPrimaryFixed"
+ android:backgroundTint="@androidprv:color/materialColorPrimaryFixedDim"
+ android:tint="@androidprv:color/materialColorOnPrimaryFixed"
android:padding="4dp"
android:src="@drawable/ic_close"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index a71782b0a109..b22355551fe3 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -30,11 +30,11 @@
android:layout_width="@dimen/connected_display_dialog_logo_size"
android:layout_height="@dimen/connected_display_dialog_logo_size"
android:background="@drawable/circular_background"
- android:backgroundTint="?androidprv:attr/materialColorSecondary"
+ android:backgroundTint="@androidprv:color/materialColorSecondary"
android:importantForAccessibility="no"
android:padding="6dp"
android:src="@drawable/stat_sys_connected_display"
- android:tint="?androidprv:attr/materialColorOnSecondary" />
+ android:tint="@androidprv:color/materialColorOnSecondary" />
<TextView
android:id="@+id/connected_display_dialog_title"
diff --git a/packages/SystemUI/res/layout/contextual_edu_dialog.xml b/packages/SystemUI/res/layout/contextual_edu_dialog.xml
index 7eb6efe4afa4..09aa8daa217e 100644
--- a/packages/SystemUI/res/layout/contextual_edu_dialog.xml
+++ b/packages/SystemUI/res/layout/contextual_edu_dialog.xml
@@ -39,6 +39,7 @@
android:ellipsize="end"
android:fontFamily="google-sans-medium"
android:maxWidth="280dp"
- android:textColor="?androidprv:attr/materialColorOnTertiaryFixed"
- android:textSize="14sp" />
+ android:textColor="@androidprv:color/materialColorOnTertiaryFixed"
+ android:textSize="14sp"
+ android:textStyle="bold" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/hearing_devices_spinner_dropdown_view.xml b/packages/SystemUI/res/layout/hearing_devices_spinner_dropdown_view.xml
index 70f2cd5fcc28..2d799ae34575 100644
--- a/packages/SystemUI/res/layout/hearing_devices_spinner_dropdown_view.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_spinner_dropdown_view.xml
@@ -28,7 +28,7 @@
android:layout_height="@dimen/hearing_devices_preset_spinner_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/hearing_devices_layout_margin"
- android:tint="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:tint="@androidprv:color/materialColorOnPrimaryContainer"
android:src="@drawable/ic_check"
android:contentDescription="@string/hearing_devices_spinner_item_selected"/>
<TextView
diff --git a/packages/SystemUI/res/layout/immersive_mode_cling.xml b/packages/SystemUI/res/layout/immersive_mode_cling.xml
index 20b7cd3add4b..f12cf96c5597 100644
--- a/packages/SystemUI/res/layout/immersive_mode_cling.xml
+++ b/packages/SystemUI/res/layout/immersive_mode_cling.xml
@@ -42,7 +42,7 @@
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:text="@string/immersive_cling_title"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="24sp"
android:fontFamily="google-sans" />
@@ -54,7 +54,7 @@
android:paddingTop="14dp"
android:gravity="center_horizontal"
android:text="@string/immersive_cling_description"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textSize="14sp"
android:fontFamily="google-sans" />
@@ -72,7 +72,7 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:text="@string/immersive_cling_positive"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
android:textAllCaps="false"
android:textSize="14sp"
android:textFontWeight="500"
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 2b40df47667a..5922a7dcdcf0 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -149,7 +149,8 @@
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="start|center_vertical">
+ android:minHeight="72dp"
+ android:gravity="center_vertical">
<TextView
android:id="@+id/mobile_title"
android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 5ab23271922c..4a40dda4a9a8 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -40,7 +40,7 @@
android:layout_height="wrap_content"
android:paddingEnd="12dp"
android:paddingBottom="4dp"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="16sp"
android:maxLines="5"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 6e7fde68ca04..3e69a6655a27 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -21,7 +21,7 @@
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="14sp"
- android:textColor="?androidprv:attr/materialColorPrimary"
+ android:textColor="@androidprv:color/materialColorPrimary"
android:importantForAccessibility="yes"
android:paddingTop="20dp"
android:paddingBottom="10dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
index 8772a732e829..5bba9ba21759 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
@@ -22,7 +22,7 @@
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:text="@string/keyboard_shortcut_join"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:singleLine="true"
android:gravity="center"
android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 42bbf25d6c26..91558fdadb36 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -25,7 +25,7 @@
android:paddingBottom="@dimen/ksh_key_view_padding_vertical"
android:layout_marginStart="@dimen/ksh_item_margin_start"
android:background="@drawable/ksh_key_item_background"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:singleLine="true"
android:gravity="center"
android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index 45a4af92339c..18716ef00815 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -52,14 +52,14 @@
android:drawableStart="@drawable/ic_shortcutlist_search"
android:drawablePadding="15dp"
android:singleLine="true"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:inputType="text"
android:textDirection="locale"
android:textAlignment="viewStart"
android:hint="@string/keyboard_shortcut_search_list_hint"
android:textAppearance="@android:style/TextAppearance.Material"
android:textSize="16sp"
- android:textColorHint="?androidprv:attr/materialColorOutline" />
+ android:textColorHint="@androidprv:color/materialColorOutline" />
<ImageButton
android:id="@+id/keyboard_shortcuts_search_cancel"
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
index 636f479e5778..e47fc62c6e16 100644
--- a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -32,7 +32,7 @@
android:layout_width="@dimen/keyguard_settings_popup_menu_icon_height"
android:layout_height="@dimen/keyguard_settings_popup_menu_icon_width"
android:layout_marginEnd="@dimen/keyguard_settings_popup_menu_icon_end_margin"
- android:tint="?androidprv:attr/materialColorOnSecondaryFixed"
+ android:tint="@androidprv:color/materialColorOnSecondaryFixed"
android:importantForAccessibility="no"
tools:ignore="UseAppTint" />
@@ -41,7 +41,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?androidprv:attr/materialColorOnSecondaryFixed"
+ android:textColor="@androidprv:color/materialColorOnSecondaryFixed"
android:textSize="14sp"
android:maxLines="1"
android:ellipsize="end" />
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 4d207da851cd..87433be45a7a 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -32,8 +32,8 @@
android:layout_marginStart="8dp"
android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
android:background="@drawable/overlay_button_background"
- android:backgroundTint="?androidprv:attr/materialColorPrimary"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:backgroundTint="@androidprv:color/materialColorPrimary"
+ android:textColor="@androidprv:color/materialColorOnPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/preview" />
@@ -47,8 +47,8 @@
android:layout_marginStart="6dp"
android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
android:background="@drawable/overlay_button_outline"
- android:backgroundTint="?androidprv:attr/materialColorPrimary"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:backgroundTint="@androidprv:color/materialColorPrimary"
+ android:textColor="@androidprv:color/materialColorOnSurface"
app:layout_constraintStart_toEndOf="@id/save"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/preview"
@@ -57,7 +57,7 @@
<ImageButton
android:id="@+id/share"
style="@android:style/Widget.Material.Button.Borderless"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
@@ -114,10 +114,10 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
- app:handleColor="?androidprv:attr/materialColorSecondary"
- app:scrimColor="?androidprv:attr/materialColorSurfaceContainer"
+ app:handleColor="@androidprv:color/materialColorSecondary"
+ app:scrimColor="@androidprv:color/materialColorSurfaceContainer"
app:scrimAlpha="128"
- app:containerBackgroundColor="?androidprv:attr/materialColorSurfaceContainer"
+ app:containerBackgroundColor="@androidprv:color/materialColorSurfaceContainer"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
@@ -131,11 +131,11 @@
app:layout_constraintTop_toTopOf="@id/preview"
app:layout_constraintLeft_toLeftOf="parent"
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
- app:handleColor="?androidprv:attr/materialColorSecondary"
- app:scrimColor="?androidprv:attr/materialColorSurfaceContainer"
+ app:handleColor="@androidprv:color/materialColorSecondary"
+ app:scrimColor="@androidprv:color/materialColorSurfaceContainer"
app:scrimAlpha="128"
app:borderThickness="4dp"
- app:borderColor="?androidprv:attr/materialColorSurfaceBright" />
+ app:borderColor="@androidprv:color/materialColorSurfaceBright" />
<ImageButton
android:id="@+id/edit"
@@ -147,7 +147,7 @@
android:background="@drawable/screenshot_edit_background"
android:src="@drawable/ic_screenshot_edit"
android:contentDescription="@string/screenshot_edit_label"
- android:tint="?androidprv:attr/materialColorOnSecondaryFixed"
+ android:tint="@androidprv:color/materialColorOnSecondaryFixed"
android:padding="16dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index d8d298573d04..9a66ca9f3baa 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -66,7 +66,7 @@
android:gravity="center_vertical|start"
android:ellipsize="end"
android:maxLines="2"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="16sp"
/>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 9ef342ce5220..b2eaa6ce92b5 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -60,7 +60,7 @@
android:ellipsize="end"
android:maxLines="1"
android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:textSize="16sp"
/>
@@ -75,7 +75,7 @@
android:maxLines="1"
android:layout_below="@id/channel_name"
android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:textSize="14sp"
/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/notification_2025_footer.xml
index 71c77a56b6a8..9b3d67f7b4a2 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
+++ b/packages/SystemUI/res/layout/notification_2025_footer.xml
@@ -64,6 +64,8 @@
android:contentDescription="@string/accessibility_clear_all"
android:focusable="true"
android:text="@string/clear_all_notifications_text"
+ android:ellipsize="end"
+ android:maxLines="1"
app:layout_constraintEnd_toStartOf="@id/settings_button"
app:layout_constraintStart_toEndOf="@id/history_button" />
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index 13e24a9ea277..c1d94f990d25 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -21,4 +21,4 @@
android:id="@+id/notification_more_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_divider_height"
- android:background="?androidprv:attr/materialColorOnSurfaceVariant" />
+ android:background="@androidprv:color/materialColorOnSurfaceVariant" />
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 3a752c81b95a..cb9d8115d674 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -174,7 +174,7 @@
android:contentDescription="@string/notification_more_settings"
android:background="@drawable/ripple_drawable_20dp"
android:src="@drawable/ic_settings"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:layout_alignParentEnd="true" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 19a3f2fd521c..edca7e3a9940 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -103,7 +103,7 @@ asked for it -->
android:contentDescription="@string/notification_app_settings"
android:src="@drawable/ic_info"
android:layout_toStartOf="@id/info"
- android:tint="?androidprv:attr/materialColorPrimary"/>
+ android:tint="@androidprv:color/materialColorPrimary"/>
<ImageButton
android:id="@+id/info"
android:layout_width="@dimen/notification_importance_toggle_size"
@@ -112,7 +112,7 @@ asked for it -->
android:contentDescription="@string/notification_more_settings"
android:background="@drawable/ripple_drawable_20dp"
android:src="@drawable/ic_settings"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:layout_alignParentEnd="true" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ce09385eaf45..f114f4cc02fa 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -23,7 +23,7 @@
android:orientation="vertical"
android:paddingTop="2dp"
android:paddingBottom="2dp"
- android:background="?androidprv:attr/materialColorSurfaceContainerHigh"
+ android:background="@androidprv:color/materialColorSurfaceContainerHigh"
android:theme="@style/Theme.SystemUI">
<RelativeLayout
@@ -38,7 +38,7 @@
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:paddingStart="@*android:dimen/notification_content_margin_end"
- android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textColor="@androidprv:color/materialColorOnSurface"
android:paddingEnd="4dp"/>
<ImageView
diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml
index fa6f965198d4..364b44c97d61 100644
--- a/packages/SystemUI/res/layout/notification_snooze_option.xml
+++ b/packages/SystemUI/res/layout/notification_snooze_option.xml
@@ -23,4 +23,4 @@
android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
android:gravity="center_vertical"
android:textSize="14sp"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"/> \ No newline at end of file
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 215e4e457060..7745af9dd408 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -49,27 +49,25 @@
ongoing_activity_chip_short_time_delta] will ever be shown at one time. -->
<!-- Shows a timer, like 00:01. -->
+ <!-- Don't use the LimitedWidth style for the timer because the end of the timer is often
+ the most important value. ChipChronometer has the correct logic for when the timer is
+ too large for the space allowed. -->
<com.android.systemui.statusbar.chips.ui.view.ChipChronometer
android:id="@+id/ongoing_activity_chip_time"
style="@style/StatusBar.Chip.Text"
/>
<!-- Shows generic text. -->
- <!-- Since there's so little room in the status bar chip area, don't ellipsize the text and
- instead just fade it out a bit at the end. -->
<TextView
android:id="@+id/ongoing_activity_chip_text"
- style="@style/StatusBar.Chip.Text"
- android:ellipsize="none"
- android:requiresFadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/ongoing_activity_chip_text_fading_edge_length"
+ style="@style/StatusBar.Chip.Text.LimitedWidth"
android:visibility="gone"
/>
<!-- Shows a time delta in short form, like "15min" or "1hr". -->
<android.widget.DateTimeView
android:id="@+id/ongoing_activity_chip_short_time_delta"
- style="@style/StatusBar.Chip.Text"
+ style="@style/StatusBar.Chip.Text.LimitedWidth"
android:visibility="gone"
/>
diff --git a/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml b/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
index b84f3a9794be..3ca4b94d3003 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
@@ -25,7 +25,7 @@
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="28dp"
app:cardElevation="0dp"
- app:cardBackgroundColor="?androidprv:attr/materialColorSurfaceBright">
+ app:cardBackgroundColor="@androidprv:color/materialColorSurfaceBright">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/privacy_dialog_v2.xml b/packages/SystemUI/res/layout/privacy_dialog_v2.xml
index 76098a1ab486..0392322aa1c1 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_v2.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_v2.xml
@@ -45,7 +45,7 @@
android:layout_height="wrap_content"
android:text="@string/privacy_dialog_summary"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
android:gravity="center"
android:layout_marginBottom="20dp"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml
index b2a8c4ce96db..76a7d3432380 100644
--- a/packages/SystemUI/res/layout/record_issue_dialog.xml
+++ b/packages/SystemUI/res/layout/record_issue_dialog.xml
@@ -55,7 +55,7 @@
android:layout_height="@dimen/screenrecord_option_icon_size"
android:layout_weight="0"
android:src="@drawable/ic_screenrecord"
- app:tint="?androidprv:attr/materialColorOnSurface"
+ app:tint="@androidprv:color/materialColorOnSurface"
android:importantForAccessibility="no"
android:layout_gravity="center"
android:layout_marginEnd="@dimen/screenrecord_option_padding" />
@@ -95,7 +95,7 @@
android:layout_height="@dimen/screenrecord_option_icon_size"
android:layout_weight="0"
android:src="@drawable/ic_bugreport"
- app:tint="?androidprv:attr/materialColorOnSurface"
+ app:tint="@androidprv:color/materialColorOnSurface"
android:importantForAccessibility="no"
android:layout_gravity="center"
android:layout_marginEnd="@dimen/screenrecord_option_padding" />
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index 4b5cdb5e708d..c6a9470a94b2 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -29,7 +29,7 @@
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center_vertical"
android:layout_weight="0"
- android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:layout_marginEnd="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<Spinner
android:id="@+id/screen_recording_options"
@@ -63,7 +63,7 @@
android:layout_height="@dimen/screenrecord_option_icon_size"
android:src="@drawable/ic_touch"
android:tint="?android:attr/textColorSecondary"
- android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:layout_marginEnd="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<TextView
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 0533c7e3fc50..78d1ab25ffa9 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -36,7 +36,7 @@
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
android:src="@drawable/ic_media_projection_permission"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/screen_share_dialog_title"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
index 8c31713a5e34..1ef010b62d81 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
@@ -39,6 +39,6 @@
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?androidprv:attr/materialColorError" />
+ android:textColor="@androidprv:color/materialColorError" />
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index fff1de7c1049..f03c0323a7b7 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -127,8 +127,8 @@
android:layout_height="match_parent"
android:layout_margin="@dimen/overlay_dismiss_button_margin"
android:background="@drawable/circular_background"
- android:backgroundTint="?androidprv:attr/materialColorPrimary"
- android:tint="?androidprv:attr/materialColorOnPrimary"
+ android:backgroundTint="@androidprv:color/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorOnPrimary"
android:padding="4dp"
android:src="@drawable/ic_close"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
index 39ec09b14157..980387176cff 100644
--- a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
+++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
@@ -38,8 +38,8 @@
android:layout_height="24dp"
android:layout_gravity="center"
android:background="@drawable/circular_background"
- android:backgroundTint="?androidprv:attr/materialColorSurfaceContainerHigh"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:backgroundTint="@androidprv:color/materialColorSurfaceContainerHigh"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:padding="2dp"
android:src="@drawable/ic_close"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/shelf_action_chip.xml b/packages/SystemUI/res/layout/shelf_action_chip.xml
index 1c65e366d619..430e9f742a07 100644
--- a/packages/SystemUI/res/layout/shelf_action_chip.xml
+++ b/packages/SystemUI/res/layout/shelf_action_chip.xml
@@ -27,7 +27,7 @@
>
<ImageView
android:id="@+id/overlay_action_chip_icon"
- android:tint="?androidprv:attr/materialColorOnSecondary"
+ android:tint="@androidprv:color/materialColorOnSecondary"
android:tintMode="src_in"
android:layout_width="@dimen/overlay_action_chip_icon_size"
android:layout_height="@dimen/overlay_action_chip_icon_size"/>
@@ -37,5 +37,5 @@
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="@dimen/overlay_action_chip_text_size"
- android:textColor="?androidprv:attr/materialColorOnSecondary"/>
+ android:textColor="@androidprv:color/materialColorOnSecondary"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index b8544a64d9da..a3bad8f012ac 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -58,7 +58,7 @@
android:contentDescription="@string/accessibility_volume_settings"
android:soundEffectsEnabled="false"
android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary"
+ android:tint="@androidprv:color/materialColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
@@ -79,4 +79,4 @@
app:layout_constraintEnd_toStartOf="@id/volume_dialog_background"
app:layout_constraintTop_toTopOf="@id/volume_dialog_main_slider_container" />
-</androidx.constraintlayout.motion.widget.MotionLayout> \ No newline at end of file
+</androidx.constraintlayout.motion.widget.MotionLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index c1852b106544..9ac456c17084 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,15 +14,21 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/volume_dialog_slider_width"
- android:layout_height="@dimen/volume_dialog_slider_height">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
<com.google.android.material.slider.Slider
- style="@style/SystemUI.Material3.Slider.Volume"
android:id="@+id/volume_dialog_slider"
- android:layout_width="@dimen/volume_dialog_slider_height"
- android:layout_height="match_parent"
+ style="@style/SystemUI.Material3.Slider.Volume"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="@dimen/volume_dialog_slider_height"
android:layout_gravity="center"
- android:rotation="270"
- android:theme="@style/Theme.Material3.Light" />
-</FrameLayout>
+ android:theme="@style/Theme.Material3.Light"
+ android:orientation="vertical"
+ app:thumbHeight="52dp"
+ app:trackCornerSize="12dp"
+ app:trackHeight="40dp"
+ app:trackStopIndicatorSize="6dp"
+ app:trackInsideCornerSize="2dp" />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_ringer_button.xml b/packages/SystemUI/res/layout/volume_ringer_button.xml
index 38bb783c2920..e65d0b938b65 100644
--- a/packages/SystemUI/res/layout/volume_ringer_button.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_button.xml
@@ -26,7 +26,7 @@
android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
android:contentDescription="@string/volume_ringer_mode"
android:gravity="center"
- android:tint="?androidprv:attr/materialColorOnSurface"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:src="@drawable/volume_ringer_item_bg"
android:background="@drawable/volume_ringer_item_bg"/>
diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
index 10768fccd4ad..28662669ec25 100644
--- a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
+++ b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
@@ -1 +1 @@
-{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":511,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":142,"cm":"drag with gesture","dr":108},{"tm":217,"cm":"onPause","dr":0},{"tm":250,"cm":"release playback realtime","dr":36}],"props":{}} \ No newline at end of file
+{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":511,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":142,"cm":"drag with gesture","dr":108},{"tm":217,"cm":"onPause","dr":0},{"tm":250,"cm":"release playback realtime","dr":36}],"props":{}} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
index 1703c41df33a..21a9e135ce8c 100644
--- a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
+++ b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
@@ -1 +1 @@
-{"v":"5.12.1","fr":60,"ip":0,"op":97,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Trackpad-JSON_Recents-EDU","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-20,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-20,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,298.112,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[-10],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[76,0,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[139,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[139,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[111,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[111,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[83,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[55,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[36,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-70.349,0.652,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[182,30],"t":217,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":292,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_4","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":53,"op":97,"st":53,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"track matte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","tt":1,"tp":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":436,"s":[100]},{"t":439,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":47,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":96,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":47,"op":97,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"track matte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":5,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":12,"op":58,"st":-235,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"track matte 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":7,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":-217,"op":33,"st":-217,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":96,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":221,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
+{"v":"5.12.1","fr":60,"ip":0,"op":97,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Trackpad-JSON_Recents-EDU","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-20,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-20,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,298.112,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[-10],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[76,0,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[139,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[139,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[111,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[111,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[83,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[55,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[36,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-70.349,0.652,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[182,30],"t":217,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":292,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_4","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":53,"op":97,"st":53,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"track matte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","tt":1,"tp":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":436,"s":[100]},{"t":439,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":47,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":96,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":47,"op":97,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"track matte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":5,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":12,"op":58,"st":-235,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"track matte 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":7,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":-217,"op":33,"st":-217,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":96,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":221,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 22d80135a2ac..1d0524a7192e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -35,14 +35,14 @@
<string name="extreme_battery_saver_text" msgid="8455810156739865335">"Ekstreem"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Outodraai skerm"</string>
<string name="usb_device_permission_prompt" msgid="4414719028369181772">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
- <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Laat <xliff:g id="APPLICATION">%1$s</xliff:g> toe om by <xliff:g id="USB_DEVICE">%2$s</xliff:g> in te gaan?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
+ <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Laat <xliff:g id="APPLICATION">%1$s</xliff:g> toe om by <xliff:g id="USB_DEVICE">%2$s</xliff:g> in te gaan?\nOpneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
<string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?"</string>
- <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Opneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem. As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
+ <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Opneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem. As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
<string name="usb_audio_device_prompt" msgid="7944987408206252949">"As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
<string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Hanteer <xliff:g id="USB_DEVICE">%2$s</xliff:g> met <xliff:g id="APPLICATION">%1$s</xliff:g>?"</string>
- <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?\nOpneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Hanteer <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> met <xliff:g id="APPLICATION">%1$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Geen geïnstalleerde programme werk met hierdie USB-toebehoorsel nie. Vind meer uit oor hierdie toebehoorsel by <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="1236358027511638648">"USB-toebehoorsel"</string>
@@ -83,7 +83,7 @@
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Toestel moet ontsluit word voordat skermkiekie gestoor kan word"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Kan nie skermkiekie stoor nie"</string>
- <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
+ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die app of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
<string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Die neem van skermskote word deur jou IT-admin geblokkeer"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Wysig"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Wysig skermkiekie"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Legstukke"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"As jy legstukke as ’n kortpad op die sluitskerm wil byvoeg, moet jy seker maak dit is in instellings geaktiveer."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Maak seker dat “Wys legstukke op sluitskerm” in instellings geaktiveer is om die “Legstukke”-kortpad by te voeg."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellings"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekke"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vee alle stil kennisgewings uit"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Maak kennisgewinginstellings oop"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kennisgewings onderbreek deur Moenie Steur Nie"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen kennisgewings nie}=1{Kennisgewings is deur {mode} onderbreek}=2{Kennisgewings is deur {mode} en een ander modus onderbreek}other{Kennisgewings is deur {mode} en # ander modusse onderbreek}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
@@ -659,21 +663,21 @@
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Oorfoonvolume het die veilige limiet vir hierdie week oorskry"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Hou aan luister"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Stel volume sagter"</string>
- <string name="screen_pinning_title" msgid="9058007390337841305">"Program is vasgespeld"</string>
+ <string name="screen_pinning_title" msgid="9058007390337841305">"App is vasgespeld"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Oorsig om dit te ontspeld."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Tuis om dit te ontspeld."</string>
<string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Dit hou dit in sig totdat jy dit ontspeld. Swiep op en hou om te ontspeld."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Oorsig om dit te ontspeld."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Tuis om dit te ontspeld."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Persoonlike data (soos kontakte en e-posinhoud) kan toeganklik wees."</string>
- <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Vasgespelde program kan ander programme oopmaak."</string>
- <string name="screen_pinning_toast" msgid="8177286912533744328">"Raak en hou die terug- en oorsigknoppie om hierdie program te ontspeld"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Raak en hou die terug- en tuisknoppie om hierdie program te ontspeld"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Swiep op en hou om hierdie program te ontspeld"</string>
+ <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Vasgespelde app kan ander apps oopmaak."</string>
+ <string name="screen_pinning_toast" msgid="8177286912533744328">"Raak en hou die terug- en oorsigknoppie om hierdie app te ontspeld"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Raak en hou die terug- en tuisknoppie om hierdie app te ontspeld"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Swiep op en hou om hierdie app te ontspeld"</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"Het dit"</string>
<string name="screen_pinning_negative" msgid="6882816864569211666">"Nee, dankie"</string>
- <string name="screen_pinning_start" msgid="7483998671383371313">"Program is vasgespeld"</string>
- <string name="screen_pinning_exit" msgid="4553787518387346893">"Program is ontspeld"</string>
+ <string name="screen_pinning_start" msgid="7483998671383371313">"App is vasgespeld"</string>
+ <string name="screen_pinning_exit" msgid="4553787518387346893">"App is ontspeld"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"Bel"</string>
<string name="stream_system" msgid="7663148785370565134">"Stelsel"</string>
<string name="stream_ring" msgid="7550670036738697526">"Lui"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kopnasporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"luiermodus"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; tik om luiermodus te verander"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Sluit skerm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Maak ’n nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Verrigting van veelvuldige take"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gebruik verdeelde skerm met huidige app aan die regterkant"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gebruik verdeelde skerm met huidige app aan die linkerkant"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skakel oor van verdeelde skerm na volskerm"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skakel oor na app regs of onder terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tydens verdeelde skerm: verplaas ’n app van een skerm na ’n ander"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Skuif aktiewe venster tussen skerms"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Skuif venster na links"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Skuif venster na regs"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maak venster groot"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Maak venster klein"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skakel oor na volgende taal"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skakel oor na vorige taal"</string>
@@ -996,7 +1000,7 @@
<string name="tuner_left" msgid="5758862558405684490">"Links"</string>
<string name="tuner_right" msgid="8247571132790812149">"Regs"</string>
<string name="tuner_menu" msgid="363690665924769420">"Kieslys"</string>
- <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g>-program"</string>
+ <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g>-app"</string>
<string name="notification_channel_alerts" msgid="3385787053375150046">"Opletberigte"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skermkiekies"</string>
@@ -1007,8 +1011,8 @@
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Toeganklikheid"</string>
<string name="instant_apps" msgid="8337185853050247304">"Kitsapps"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> loop tans"</string>
- <string name="instant_apps_message" msgid="6112428971833011754">"Program is oopgemaak sonder dat dit geïnstalleer is."</string>
- <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Program is oopgemaak sonder dat dit geïnstalleer is. Tik om meer te wete te kom."</string>
+ <string name="instant_apps_message" msgid="6112428971833011754">"App is oopgemaak sonder dat dit geïnstalleer is."</string>
+ <string name="instant_apps_message_with_help" msgid="1816952263531203932">"App is oopgemaak sonder dat dit geïnstalleer is. Tik om meer inligting te kry."</string>
<string name="app_info" msgid="5153758994129963243">"Appinligting"</string>
<string name="go_to_web" msgid="636673528981366511">"Gaan na blaaier"</string>
<string name="mobile_data" msgid="4564407557775397216">"Mobiele data"</string>
@@ -1019,8 +1023,8 @@
<string name="dnd_is_off" msgid="3185706903793094463">"Moenie Steur Nie is af"</string>
<string name="dnd_is_on" msgid="7009368176361546279">"Moenie Steur Nie is aan"</string>
<string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"\'n Outomatiese reël (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
- <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"\'n Program (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"\'n Outomatiese reël of program het Moenie Steur Nie aangeskakel."</string>
+ <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"\'n App (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"\'n Outomatiese reël of app het Moenie Steur Nie aangeskakel."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"Programme wat op die agtergrond loop"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Tik vir besonderhede oor battery- en datagebruik"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Skakel mobiele data af?"</string>
@@ -1030,11 +1034,11 @@
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiele data sal nie outomaties op grond van beskikbaarheid oorskakel nie"</string>
<string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nee, dankie"</string>
<string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, skakel oor"</string>
- <string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n program \'n toestemmingversoek verberg."</string>
+ <string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n app \'n toestemmingversoek verberg."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Laat <xliff:g id="APP_0">%1$s</xliff:g> toe om <xliff:g id="APP_2">%2$s</xliff:g>-skyfies te wys?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Dit kan inligting van <xliff:g id="APP">%1$s</xliff:g> af lees"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"– Dit kan handelinge binne <xliff:g id="APP">%1$s</xliff:g> uitvoer"</string>
- <string name="slice_permission_checkbox" msgid="4242888137592298523">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om skyfies uit enige program te gebruik"</string>
+ <string name="slice_permission_checkbox" msgid="4242888137592298523">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om skyfies uit enige app te gebruik"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Laat toe"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Weier"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"Tik om Batterybespaarder te skeduleer"</string>
@@ -1114,7 +1118,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"wissel"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"Wysig"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Kies app om kontroles by te voeg"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrole bygevoeg.}other{# kontroles bygevoeg.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Verwyder"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"Voeg <xliff:g id="APPNAME">%s</xliff:g> by?"</string>
@@ -1135,7 +1139,7 @@
<string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Herrangskik"</string>
<string name="controls_favorite_add_controls" msgid="1221420435546694004">"Voeg kontroles by"</string>
<string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Terug na wysiging"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroles kon nie gelaai word nie. Gaan die <xliff:g id="APP">%s</xliff:g>-program na om seker te maak dat die programinstellings nie verander het nie."</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroles kon nie gelaai word nie. Gaan die <xliff:g id="APP">%s</xliff:g>-app na om seker te maak dat die appinstellings nie verander het nie."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Versoenbare kontroles is nie beskikbaar nie"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Ander"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Voeg by toestelkontroles"</string>
@@ -1186,10 +1190,10 @@
<string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Saai jou media uit"</string>
<string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Saai tans <xliff:g id="APP_LABEL">%1$s</xliff:g> uit"</string>
- <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
+ <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan app na"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrole is nie beskikbaar nie"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"Kon nie by <xliff:g id="DEVICE">%1$s</xliff:g> ingaan nie. Gaan die <xliff:g id="APPLICATION">%2$s</xliff:g>-program na om seker te maak dat die kontrole steeds beskikbaar is en dat die programinstellings nie verander het nie."</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"Kon nie by <xliff:g id="DEVICE">%1$s</xliff:g> ingaan nie. Gaan die <xliff:g id="APPLICATION">%2$s</xliff:g>-app na om seker te maak dat die kontrole steeds beskikbaar is en dat die appinstellings nie verander het nie."</string>
<string name="controls_open_app" msgid="483650971094300141">"Maak app oop"</string>
<string name="controls_error_generic" msgid="352500456918362905">"Kan nie status laai nie"</string>
<string name="controls_error_failed" msgid="960228639198558525">"Fout, probeer weer"</string>
@@ -1204,8 +1208,8 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ontkoppel)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan nie wissel nie. Tik om weer te probeer."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Koppel ’n toestel"</string>
- <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die program oop om hierdie sessie uit te saai."</string>
- <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende program"</string>
+ <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die app oop om hierdie sessie uit te saai."</string>
+ <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hou op uitsaai"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Beskikbare toestelle vir oudio-uitsette."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Stelselkontroles"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Stelselapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Onlangse apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Verdeelde skerm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pasmaak kortpadsleutels"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Verwyder kortpad?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Stel terug na verstek?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Druk sleutel om kortpad toe te wys"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dit sal jou gepasmaakte kortpad permanent uitvee."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dit sal al jou gepasmaakte kortpaaie permanent uitvee."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikoon vir Handeling- of Meta-sleutel"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pasmaak"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Stel terug"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"vorentoe-skuinsstreep"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stel kortpad"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Verwyder"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, stel terug"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselleer"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk sleutel"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Sleutelkombinasie is reeds in gebruik. Probeer ’n ander sleutel."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Welgedaan!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jy het die Bekyk Onlangse Apps-gebaar voltooi"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutoriaalanimasie; klik om te onderbreek of hervat om te speel."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index e36aab27d541..687bd089d572 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ምግብሮች"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ማያ ገጽ ቁልፍ ላይ ምግብሮችን እንደ አቋራጭ ለማከል በቅንብሮች ውስጥ መንቃቱን ያረጋግጡ።"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"የ«ምግብሮች» አቋራጭን ለማከል በቅንብሮች ውስጥ «ምግብሮችን በማያ ገፅ ቁልፍ ላይ አሳይ» የሚለው መንቃቱን ያረጋግጡ።"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ቅንብሮች"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ማሳወቂያዎች"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ውይይቶች"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ሁሉንም ጸጥ ያሉ ማሳወቂያዎችን ያጽዱ"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"የማሳወቂያ ቅንብሮችን ክፈት"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ማሳወቂያዎች በአትረብሽ ባሉበት ቆመዋል"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ምንም ማሳወቂያዎች የሉም}=1{ማሳወቂያዎች በ{mode} ባሉበት ቆመዋል}=2{ማሳወቂያዎች በ{mode} እና አንድ ሌላ ሁነታ ባሉበት ቆመዋል}one{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታ ባሉበት ቆመዋል}other{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታዎች ባሉበት ቆመዋል}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"የጭንቅላት ክትትል"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ደዋይ ሁነታ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>፣ የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ማያ ገፅ ቁልፍ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ማስታወሻ ይውሰዱ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"የአሁኑ መተግበሪያ በስተቀኝ ላይ ሆኖ የተከፈለ ማያ ገጽን ይጠቀሙ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"የአሁኑ መተግበሪያ በስተግራ ላይ ሆኖ የተከፈለ ማያ ገጽን ይጠቀሙ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ከየተከፈለ ማያ ገጽ ወደ ሙሉ ገጽ ዕይታ ቀይር"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከታች ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"በተከፈለ ማያ ገጽ ወቅት፡- መተግበሪያን ከአንዱ ወደ ሌላው ተካ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"በማሳያዎች መካከል ንቁ መስኮትን ያንቀሳቅሱ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"መስኮትን ወደ ግራ አሳንስ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"መስኮትን ወደ ቀኝ አሳንስ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"መስኮትን አሳድግ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"መስኮት አሳንስ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ግቤት"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ወደ ቀጣዩ ቋንቋ ቀይር"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ወደ ቀዳሚ ቋንቋ ቀይር"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"የሥርዓት መቆጣጠሪያዎች"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"የሥርዓት መተግበሪያዎች"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"የቅርብ ጊዜ መተግበሪያዎች"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"የተከፈለ ማያ ገፅ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ግብዓት"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"የመተግበሪያ አቋራጮች"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"የቁልፍ ሰሌዳ አቋራጮችን ያብጁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"አቋራጭ ይወገድ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ወደ ነባሪ ዳግም ይጀመር?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"አቋራጭ ለመመደብ ቁልፍ ይጫኑ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ይህ ብጁ አቋራጭዎን በቋሚነት ይሰርዛል።"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ይህ ሁሉንም ብጁ አቋራጮችዎን በቋሚነት ይሰርዛል።"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"የእርምጃ ወይም ሜታ ቁልፍ አዶ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"የመደመር አዶ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"አብጅ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ዳግም አስጀምር"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ተከናውኗል"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ሲደመር"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ወደፊት ህዝባር"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"አቋራጭ አቀናብር"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"አስወግድ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"አዎ፣ ዳግም አስጀምር"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ይቅር"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ቁልፍ ይጫኑ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"የቁልፍ ጥምረት አስቀድሞ በሥራ ላይ ነው። ሌላ ቁልፍ ይሞክሩ።"</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ጥሩ ሠርተዋል!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"የሁሉንም መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"የአጋዥ ሥልጠና እነማ፣ ማጫወትን ባለበት ለማቆም እና ከቆመበት ለመቀጠል ጠቅ ያድርጉ።"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index d6e91cc9c3c2..0fc6598d404d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"التطبيقات المصغَّرة"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"لإضافة التطبيقات المصغّرة على شاشة القفل كاختصار، تأكَّد من تفعيلها في الإعدادات."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"لإضافة اختصار \"التطبيقات المصغّرة\"، يجب تفعيل خيار \"عرض التطبيقات المصغّرة على شاشة القفل\" من خلال الإعدادات."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"الإعدادات"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"زر \"إظهار شاشة الاستراحة\""</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -590,6 +592,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"المحادثات"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"محو جميع الإشعارات الصامتة"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"تم إيقاف الإشعارات مؤقتًا وفقًا لإعداد \"عدم الإزعاج\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ما مِن إشعارات}=1{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\"}=2{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" ووضع واحد آخر}few{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# أوضاع أخرى}many{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# وضعًا آخر}other{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# وضع آخر}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"تتبُّع حركة الرأس"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"الحالة: <xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. انقر لتغيير وضع الرنين"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
@@ -868,9 +873,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"شاشة القفل"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"تدوين ملاحظة"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"تعدُّد المهام"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق الحالي على اليمين"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق الحالي على اليسار"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"التبديل من وضع \"تقسيم الشاشة\" إلى وضع \"ملء الشاشة\""</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"التبديل إلى التطبيق على اليسار أو الأسفل أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"التبديل إلى التطبيق على اليمين أو الأعلى أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"استبدال تطبيق بآخر في وضع \"تقسيم الشاشة\""</string>
@@ -1415,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"عناصر التحكّم في النظام"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"تطبيقات النظام"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"تعدُّد المهام"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"التطبيقات المستخدمة مؤخرًا"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"تقسيم الشاشة"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"الإدخال"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"اختصارات التطبيقات"</string>
@@ -1424,14 +1431,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"تخصيص اختصارات لوحة المفاتيح"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"هل تريد إزالة هذا الاختصار؟"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"يُرجى تأكيد إعادة الضبط على الإعدادات التلقائية"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"اضغط على مفتاح لتخصيص الاختصار"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"سيؤدي هذا الإجراء إلى حذف الاختصار المخصّص نهائيًا."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"سيؤدي هذا الإجراء إلى حذف جميع الاختصارات المخصّصة نهائيًا."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"البحث في الاختصارات"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"‏رمز مفتاح الإجراء (مفتاح Meta)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"رمز علامة الجمع (+)"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"تخصيص"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"إعادة الضبط"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تم"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
@@ -1441,6 +1451,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ضبط الاختصار"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"إزالة"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"نعم، أريد إعادة الضبط"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"إلغاء"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"اضغط على مفتاح"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"يتم حاليًا استخدام مجموعة المفاتيح هذه. يُرجى تجربة مفتاح آخر."</string>
@@ -1472,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"أحسنت!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"لقد أكملْت التدريب على إيماءة عرض جميع التطبيقات"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"صورة متحركة للدليل التوجيهي: يمكنك النقر عليها لإيقاف تشغيلها مؤقتًا واستئنافه."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"‏مستوى الإضاءة: %1$d من %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8f5b1e18f626..b0a9d0960087 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ৱিজেট"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"লক স্ক্ৰীনৰ ৱিজেট সুবিধাটো শ্বৰ্টকাট হিচাপে যোগ দিবলৈ ছেটিঙত সেয়া সক্ষম হৈ থকাটো নিশ্চিত কৰক।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ৱিজেট\"ৰ শ্বৰ্টকাট যোগ দিবলৈ, ছেটিঙত \"লক স্ক্ৰীনত ৱিজেট দেখুৱাওক\" সক্ষম কৰি থোৱাটো নিশ্চিত কৰক।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ছেটিং"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"স্ক্ৰীনছেভাৰৰ বুটাম দেখুৱাওক"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"জাননীসমূহ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"বাৰ্তালাপ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"আটাইবোৰ নীৰৱ জাননী মচক"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"জাননীৰ ছেটিং খোলক"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"অসুবিধা নিদিব-ই জাননী পজ কৰিছে"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনো জাননী নাই}=1{{mode}এ জাননী পজ কৰিছে}=2{{mode} আৰু আন এটা ম’ডে জাননী পজ কৰিছে}one{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}other{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হে’ড ট্ৰেকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ৰিংগাৰ ম’ড"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্ৰীন"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"টোকা লিখক"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"মাল্টিটাস্কিং"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"বৰ্তমানৰ এপ্‌টোৰ সৈতে সোঁফালে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"বৰ্তমানৰ এপ্‌টোৰ সৈতে বাওঁফালে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"বিভাজিত স্ক্ৰীনৰ পৰা পূৰ্ণ স্ক্ৰীনলৈ সলনি কৰক"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত সোঁফালে অথবা তলত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"বিভাজিত স্ক্ৰীনৰ ব্যৱহাৰ কৰাৰ সময়ত: কোনো এপ্ এখন স্ক্ৰীনৰ পৰা আনখনলৈ নিয়ক"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ডিছপ্লে’সমূহৰ মাজত সক্রিয় হৈ থকা ৱিণ্ড’ সলনা সলনিকৈ ব্যৱহাৰ কৰক"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ৱিণ্ড’ বাওঁফাললৈ স্থানান্তৰ কৰক"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ৱিণ্ড’ সোঁফাললৈ স্থানান্তৰ কৰক"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ৱিণ্ড’ মেক্সিমাইজ কৰক"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ৱিণ্ড’ মিনিমাইজ কৰক"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"পৰৱৰ্তী ভাষাটোলৈ সলনি কৰক"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"পূৰ্বৰ ভাষালৈ সলনি কৰক"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ছিষ্টেমৰ নিয়ন্ত্ৰণ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ছিষ্টেম এপ্‌"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"শেহতীয়া এপ্‌সমূহ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"বিভাজিত স্ক্ৰীন"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"এপ্ শ্বৰ্টকাটসমূহ"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীব’ৰ্ডৰ শ্বৰ্টকাট কাষ্টমাইজ কৰক"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শ্বৰ্টকাট আঁতৰাবনে?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ডিফ\'ল্ট হিচাপে পুনৰ ৰিছেট কৰিবনে?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"শ্বৰ্টকাটৰ ভূমিকা অৰ্পণ কৰিবলৈ কী টিপক"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এইটোৱে আপোনাৰ কাষ্টম শ্বৰ্টকাট মচিব।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"এইটোৱে আপোনাৰ আটাইবোৰ কাষ্টম শ্বৰ্টকাট স্থায়ীভাৱে মচিব।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"কাৰ্য বা মেটা কীৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"যোগ চিনৰ চিহ্ন"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাষ্টমাইজ কৰক"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ৰিছেট কৰক"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হ’ল"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"যোগ চিন"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ফৰৱাৰ্ড শ্লেশ্ব"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শ্বৰ্টকাট ছেট কৰক"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"আঁতৰাওক"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"হয়, ৰিছেট কৰক"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল কৰক"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী টিপক"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"কীৰ মিশ্ৰণ ইতিমধ্যে ব্যৱহাৰ হৈ আছে। অন্য এটা কী ব্যৱহাৰ কৰি চাওক।"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"বঢ়িয়া!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপুনি আটাইবোৰ এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"টিউট’ৰিয়েল এনিমেশ্বন, পজ কৰিবলৈ আৰু প্লে’ কৰাটো পুনৰ আৰম্ভ কৰিবলৈ ক্লিক কৰক।"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 1126da15496d..0f59bfbc86df 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidcetlər"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Kilidli ekrana qısayol kimi Vidcet əlavə etmək üçün onun ayarlarda aktiv olduğundan əmin olun."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Vidcetlər\" qısayolunu əlavə etmək üçün ayarlarda \"Vidcetləri kilidli ekranda göstərin\" seçimi aktiv olmalıdır."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Söhbətlər"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Səssiz bildirişlərin hamısını silin"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Bildiriş ayarlarını açın"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bildirişlər \"Narahat Etməyin\" rejimi tərəfindən dayandırıldı"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildiriş yoxdur}=1{Bildirişlər {mode} tərəfindən dayandırıldı}=2{Bildirişlər {mode} və digər rejim tərəfindən dayandırıldı}other{Bildirişlər {mode} və # digər rejim tərəfindən dayandırıldı}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş izləməsi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"zəng səsi rejimi"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, zəng səsi rejimini dəyişmək üçün toxunun"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilid ekranı"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Qeyd götürün"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoxsaylı tapşırıq icrası"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Cari tətbiq sağda olmaqla bölünmüş ekrandan istifadə edin"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Cari tətbiq solda olmaqla bölünmüş ekrandan istifadə edin"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana keçin"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran istifadə edərkən sağda və ya aşağıda tətbiqə keçin"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran istifadə edərkən solda və ya yuxarıda tətbiqə keçin"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran rejimində: tətbiqi birindən digərinə dəyişin"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktiv pəncərəni displeylər arasında hərəkət etdirin"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pəncərəni sola hərəkət etdirin"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pəncərəni sağa hərəkət etdirin"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Pəncərəni böyüdün"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pəncərəni minimallaşdırın"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Daxiletmə"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Növbəti dilə keçin"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Əvvəlki dilə keçin"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistem nizamlayıcıları"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem tətbiqləri"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Son tətbiqlər"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatura qısayollarını fərdiləşdirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Qısayol silinsin?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Defolt vəziyyətə qaytarılsın?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Qısayol təyin etmək üçün düyməni basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu, fərdi qısayolunuzu həmişəlik siləcək."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu, bütün fərdi qısayollarınızı həmişəlik siləcək."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Əməliyyat və ya Meta düyməsi ikonası"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Üstəgəl ikonası"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Fərdiləşdirin"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Sıfırlayın"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hazırdır"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"irəli sləş"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Qısayol ayarlayın"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Silin"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bəli, sıfırlayın"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ləğv edin"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Düyməni basın"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Düymə kombinasiyası artıq istifadə olunur. Başqa düyməni sınayın."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Əla!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\"Bütün tətbiqlərə baxın\" jestini tamamladınız"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Öyrədici animasiya, oxudulmanı durdurmaq və davam etdirmək üçün klikləyin."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a996c8a6a6b9..a339b114f798 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da biste dodali vidžete na zaključani ekran kao prečicu, uverite se da je to omogućeno u podešavanjima."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečicu Vidžeti, uverite se da je u podešavanjima omogućeno Prikazuj vidžete na zaključanom ekranu."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Podešavanja"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Dugme Prikaži čuvar ekrana"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzacije"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obrišite sva nečujna obaveštenja"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvorite podešavanja obaveštenja"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obaveštenja su pauzirana režimom Ne uznemiravaj"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obaveštenja}=1{Obaveštenja je pauzirao {mode}}=2{Obaveštenja su pauzirali {mode} i još jedan režim}one{Obaveštenja su pauzirali {mode} i još # režim}few{Obaveštenja su pauzirali {mode} i još # režima}other{Obaveštenja su pauzirali {mode} i još # režima}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvona"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dodirnite da biste promenili režim zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Otključavanje ekrana"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Napravi belešku"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka istovremeno"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristi podeljeni ekran sa tom aplikacijom s desne strane"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristi podeljeni ekran sa tom aplikacijom s leve strane"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Pređi sa podeljenog ekrana na ceo ekran"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređi u aplikaciju zdesna ili ispod dok je podeljen ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premesti aktivan prozor na sledeći ekran"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pomerite prozor nalevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pomerite prozor nadesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Povećajte prozor"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Smanjite prozor"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pređi na sledeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pređi na prethodni jezik"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemske kontrole"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka istovremeno"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podeljeni ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice za aplikacije"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite tasterske prečice"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite da uklonite prečicu?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite da resetujete na podrazumevano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite taster da biste dodelili prečicu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovim ćete trajno izbrisati prilagođenu prečicu."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time ćete trajno izbrisati sve prilagođene prečice."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pretražite prečice"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tastera za radnju ili meta tastera"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetuj"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta unapred"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Podešavanja tastature"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Podesi prečicu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, resetuj"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite taster"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tastera se već koristi. Probajte sa drugim tasterom."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dovršili ste pokret za prikazivanje svih aplikacija."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija vodiča, kliknite da biste pauzirali i nastavili reprodukciju."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index c14d1b5ee7c9..9c5a4f287137 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджэты"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Каб можна было дадаць віджэты на экран блакіроўкі ў якасці спалучэння клавіш, яны павінны быць уключаны ў наладах."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Каб дадаць спалучэнне клавіш \"Віджэты\", у наладах павінна быць уключана функцыя \"Паказваць віджэты на экране блакіроўкі\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налады"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Апавяшчэнні"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Размовы"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Выдаліць усе апавяшчэнні без гуку"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Адкрыць налады апавяшчэнняў"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Паказ апавяшчэнняў прыпынены ў рэжыме \"Не турбаваць\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Апавяшчэнняў няма}=1{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\"}=2{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ адным рэжымам}one{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымам}few{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}many{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}other{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжыму}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Адсочваць рух галавы"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Націсніце, каб змяніць рэжым званка"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"рэжым званка"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: націсніце, каб змяніць рэжым званка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экран блакіроўкі"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Стварыць нататку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Шматзадачнасць"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Падзяліць экран і памясціць гэту праграму справа"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Падзяліць экран і памясціць гэту праграму злева"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Пераключыцца з рэжыму падзеленага экрана на поўнаэкранны рэжым"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пераключыцца на праграму справа або ўнізе на падзеленым экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У рэжыме падзеленага экрана замяніць адну праграму на іншую"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Перамясціць актыўнае акно паміж дысплэямі"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Перамясціць акно ўлева"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Перамясціць акно ўправа"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Разгарнуць акно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Згарнуць акно"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Увод"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Пераключыцца на наступную мову"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пераключыцца на папярэднюю мову"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Элементы кіравання сістэмай"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Сістэмныя праграмы"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Шматзадачнасць"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Нядаўнія праграмы"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Падзелены экран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Увод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыкі праграм"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Наладзіць спалучэнні клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Выдаліць спалучэнне клавіш?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Скінуць налады да стандартных?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Націсніце клавішу, каб прызначыць спалучэнне клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Гэта дзеянне назаўсёды выдаліць прызначанае вамі спалучэнне клавіш."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усе карыстальніцкія спалучэнні клавіш будуць назаўсёды выдалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавішы дзеяння (мета-клавішы)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Наладзіць"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Скінуць"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Гатова"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"+"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"касая рыса ўправа"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Наладзіць спалучэнне клавіш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Выдаліць"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, скінуць"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасаваць"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Націсніце клавішу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Гэта спалучэнне клавіш ужо выкарыстоўваецца. Паспрабуйце іншую клавішу."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Выдатна!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы навучыліся рабіць жэст для прагляду ўсіх праграм"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анімацыя ў дапаможніку: націсніце, каб прыпыніць ці ўзнавіць прайграванне."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 73de179729d7..ac2edfe1fc61 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Приспособления"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"За да добавите приспособления към заключения екран като пряк път, уверете се, че са активирани в настройките."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да добавите пряк път към „Приспособления“, уверете се, че опцията „Показване на приспособленията на заключения екран“ е активирана в настройките."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Бутон за показване на скрийнсейвъра"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известия"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Изчистване на всички беззвучни известия"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Отваряне на настройките за известията"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известията са поставени на пауза от режима „Не безпокойте“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Няма известия}=1{Известията са поставени на пауза от {mode}}=2{Известията са поставени на пауза от {mode} и един друг режим}other{Известията са поставени на пауза от {mode} и # други режима}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Прослед. на движенията на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Докоснете, за да промените режима на звънене"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим на звънене"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: докоснете, за да промените режима на звънене"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заключване на екрана"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Създаване на бележка"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Няколко задачи едновременно"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Използване на разделен екран с текущото приложение вдясно"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Използване на разделен екран с текущото приложение вляво"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Превключване от разделен към цял екран"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Превключване към приложението вдясно/отдолу в режима на разделен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При разделен екран: замяна на дадено приложение с друго"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Преместване на активния прозорец между екраните"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Преместване на прозореца наляво"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Преместване на прозореца надясно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Увеличаване на прозореца"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Намаляване на прозореца"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Въвеждане"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Превключване към следващия език"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Превключване към предишния език"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системни контроли"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системни приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Няколко задачи едновременно"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Скорошни приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделен екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Преки пътища към приложения"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Персонализиране на клавишните комбинации"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се премахне ли клавишната комбинация?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се възстановят ли стандартните настройки?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натиснете клавиш, за да зададете клавишна комбинация"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Това ще изтрие персонализираната клавишна комбинация за постоянно."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Това ще изтрие всичките ви персонализирани преки пътища за постоянно."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона на клавиша за действия или клавиша Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона на плюс"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Персонализиране"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Нулиране"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"наклонена черта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задаване на клавишна комбинация"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Премахване"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, нулиране"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отказ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натиснете клавиш"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Клавишната комбинация вече се използва. Опитайте с друг клавиш."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натиснете клавиша за действия на клавиатурата си"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Изпълнихте жеста за преглед на всички приложения"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимация за урока. Кликнете, за да поставите на пауза и да възобновите възпроизвеждането."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index edbf73e8f404..c35c62aec312 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"উইজেট"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"শর্টকাট হিসেবে লক স্ক্রিনে উইজেট যোগ করলে, সেটি সেটিংস থেকে চালু আছে কিনা ভালো করে দেখে নিন।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"উইজেট\" শর্টকার্ট যোগ করতে, সেটিংস থেকে \"লক স্ক্রিনে উইজেট দেখুন\" বিকল্প চালু আছে কিনা তা নিশ্চিত করুন।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"সেটিংস"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"বিজ্ঞপ্তি"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"কথোপকথন"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"সব নীরব বিজ্ঞপ্তি মুছুন"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"বিজ্ঞপ্তির সেটিংস খুলুন"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'বিরক্ত করবে না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনও বিজ্ঞপ্তি নেই}=1{{mode}-এর জন্য বিজ্ঞপ্তি পজ করা হয়েছে}=2{{mode} ও অন্য আরেকটি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}one{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}other{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হেড ট্র্যাকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"রিঙ্গার মোড"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্রিন"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"একটি নোট লিখুন"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"মাল্টিটাস্কিং"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ডানদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"বাঁদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"\'স্প্লিট স্ক্রিন\' থেকে ফুল স্ক্রিনে পাল্টান"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"স্প্লিট স্ক্রিন ব্যবহার করার সময় ডানদিকের বা নিচের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"\'স্প্লিট স্ক্রিন\' থাকাকালীন: একটি অ্যাপ থেকে অন্যটিতে পাল্টান"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ডিসপ্লের মধ্যে একটি থেকে অপরটিতে অ্যাক্টিভ উইন্ডোটি সরান"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"উইন্ডো বাঁদিকে সরান"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"উইন্ডো ডানদিকে সরান"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"উইন্ডো বড় করুন"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"উইন্ডো ছোট করুন"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"পরবর্তী ভাষায় পাল্টান"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"আগের ভাষায় পাল্টান"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"সিস্টেম কন্ট্রোল"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"সিস্টেম অ্যাপ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"সম্প্রতি ব্যবহার করা অ্যাপ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"স্প্লিট স্ক্রিন"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"অ্যাপ শর্টকাট"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীবোর্ড শর্টকাট কাস্টমাইজ করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শর্টকাট সরাবেন?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ডিফল্ট শর্টকার্ট আবার রিসেট করতে চান?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"শর্টকাট অ্যাসাইন করতে কী প্রেস করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এটি আপনার কাস্টম শর্টকাট স্থায়ীভাবে মুছে ফেলবে।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"এটি আপনার সব কাস্টম শর্টকার্ট স্থায়ীভাবে মুছে দেবে।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"শর্টকাট সার্চ করুন"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"অ্যাকশন বা মেটা কী আইকন"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"প্লাস আইকন"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাস্টমাইজ করুন"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"রিসেট করুন"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হয়ে গেছে"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"যোগ চিহ্ন"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ফরওয়ার্ড স্ল্যাশ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শর্টকাট সেট করুন"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"সরান"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"হ্যাঁ, রিসেট করতে চাই"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল করুন"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী প্রেস করুন"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"কী কম্বিনেশন আগে থেকে ব্যবহার হচ্ছে। অন্য কী ব্যবহার করে দেখুন।"</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"দারুণ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপনি \'সব অ্যাপের জেসচার দেখুন\' টিউটোরিয়াল সম্পূর্ণ করেছেন"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"টিউটোরিয়াল অ্যানিমেশন পজ করুন এবং আবার চালু করতে ক্লিক করুন।"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ab5ffaea94fd..f842aabe4514 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da dodate vidžete na zaključani ekran kao prečicu, provjerite je li ovo omogućeno u postavkama."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da dodate prečicu \"Vidžeti\", provjerite je li u postavkama omogućeno \"Prikazuj vidžete na zaključanom ekranu\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Prikaži gumb čuvara zaslona"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obriši sva nečujna obavještenja"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvaranje postavki obavještenja"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obavještenja su pauzirana načinom rada Ne ometaj"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavještenja}=1{Obavještenja su pauzirana putem načina rada {mode}}=2{Obavještenja su pauzirana putem načina rada {mode} i još jednog načina rada}one{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}few{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}other{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način rada za zvuk zvona"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; promjena načina rada zvuka zvona dodirom"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prebacivanje s podijeljenog ekrana na prikaz preko cijelog ekrana"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak u aplikaciju desno ili ispod uz podijeljeni ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Za vrijeme podijeljenog ekrana: zamjena jedne aplikacije drugom"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premještanje aktivnog prozora između ekrana"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pomicanje prozora ulijevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pomicanje prozora udesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimiziranje prozora"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimiziranje prozora"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prebacivanje na sljedeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prebacivanje na prethodni jezik"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemske kontrole"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ukloniti prečicu?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vratiti na zadano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipku da dodijelite prečicu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovo će trajno izbrisati prilagođenu prečicu."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ovo će trajno izbrisati sve vaše prilagođene prečice."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke radnji ili meta tipka"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Poništavanje"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tastature"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavi prečicu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ta se kombinacija tipki već koristi. Pokušajte s drugom tipkom."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvršili ste pokret za prikaz svih aplikacija"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija vodiča; pauziranje i nastavak reprodukcije klikom."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 152e1adc72a9..3386dc0775ca 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Per afegir widgets a la pantalla de bloqueig com a drecera, assegura\'t que estiguin activats a la configuració."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per afegir la drecera Widgets, assegura\'t que l\'opció Mostra els widgets a la pantalla de bloqueig estigui activada a la configuració."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuració"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botó Mostra l\'estalvi de pantalla"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Converses"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Esborra totes les notificacions silencioses"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Obre la configuració de notificacions"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificacions pausades pel mode No molestis"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hi ha cap notificació}=1{{mode} ha posat en pausa les notificacions}=2{{mode} i un altre mode han posat en pausa les notificacions}many{{mode} i # de modes més han posat en pausa les notificacions}other{{mode} i # modes més han posat en pausa les notificacions}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de timbre"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; toca per canviar el mode de timbre."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloqueja la pantalla"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crea una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasca"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilitza la pantalla dividida amb l\'aplicació actual a la dreta"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilitza la pantalla dividida amb l\'aplicació actual a l\'esquerra"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Canvia de pantalla dividida a pantalla completa"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Canvia a l\'aplicació de la dreta o de sota amb la pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durant el mode de pantalla dividida: substitueix una app per una altra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mou la finestra activa entre pantalles"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mou la finestra cap a l\'esquerra"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mou la finestra cap a la dreta"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximitza la finestra"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimitza la finestra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Canvia a l\'idioma següent"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Caniva a l\'idioma anterior"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controls del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacions del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicacions recents"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalitza les tecles de drecera"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vols suprimir la drecera?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vols restablir els valors predeterminats?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Prem la tecla per assignar la drecera"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Aquesta acció suprimirà la drecera personalitzada permanentment."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Aquesta acció suprimirà totes les dreceres personalitzades permanentment."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona de la tecla d\'acció o Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona del signe més"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalitza"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restableix"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fet"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"més"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra inclinada"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuració del teclat"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configura la drecera"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Suprimeix"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restableix"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel·la"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prem una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinació de tecles ja s\'està utilitzant. Prova-ho amb una altra tecla."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Enhorabona!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completat el gest per veure totes les aplicacions"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animació del tutorial; fes clic per posar en pausa i reprendre la reproducció."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ac0c6d66fc29..c5aea85af4c7 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgety"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pokud chcete přidat Widgety na obrazovku uzamčení jako zkratku, musí to být povoleno v nastavení."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pokud chcete přidat zkratku Widgety, zapněte v nastavení možnost Zobrazovat widgety na obrazovce uzamčení."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavení"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Zobrazit tlačítko spořiče obrazovky"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzace"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazat všechna tichá oznámení"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otevřít nastavení oznámení"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Oznámení jsou pozastavena režimem Nerušit"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žádná oznámení}=1{Oznámení jsou pozastavená režimem {mode}}=2{Oznámení jsou pozastavená režimem {mode} a 1 dalším}few{Oznámení jsou pozastavená režimem {mode} a # dalšími}many{Oznámení jsou pozastavená režimem {mode} a # dalšího}other{Oznámení jsou pozastavená režimem {mode} a # dalšími}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledování hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim vyzvánění"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, klepnutím změníte režim vyzvánění"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknout obrazovku"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Vytvořit poznámku"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použít rozdělenou obrazovku se stávající aplikací vpravo"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použít rozdělenou obrazovku se stávající aplikací vlevo"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Přepnout z rozdělené obrazovky na celou obrazovku"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přepnout na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přepnout na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"V režimu rozdělené obrazovky: nahradit jednu aplikaci druhou"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Přesunout aktivní okno mezi obrazovkami"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Přesunout okno doleva"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Přesunout okno doprava"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximalizovat okno"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizovat okno"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Přepnout na další jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Přepnout na předchozí jazyk"</string>
@@ -1391,7 +1394,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Je zjištěna přítomnost uživatele"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
<string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Pokračujte přejetím"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Pokračovat přejetím nahoru"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnitřní displej bude zrcadlen. Přední displej bude vypnutý."</string>
<string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Ovládací prvky systému"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikace"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Poslední aplikace"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdělená obrazovka"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstrabit zkratku?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetovat do výchozího nastavení?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nastavte zkratku stisknutím klávesy"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Vlastní zkratka se trvale smaže."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tím trvale odstraníte všechny své vlastní zkratky."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klávesy Akce nebo Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Přizpůsobit"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetovat"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"lomítko"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavit zkratku"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstranit"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ano, resetovat"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušit"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stiskněte klávesu"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinace kláves se už používá. Použijte jinou klávesu."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Provedli jste gesto k zobrazení všech aplikací"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Výuková animace, kliknutím pozastavíte nebo obnovíte."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 968d44c6b597..168afad1817c 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Hvis du vil tilføje widgets på låseskærmen som en genvej, skal du sørge for, at funktionen er aktiveret i indstillingerne."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Hvis du vil tilføje genvejen \"Widgets\", skal du sørge for, at \"Vis widgets på låseskærmen\" er aktiveret i indstillingerne."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Indstillinger"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ryd alle lydløse notifikationer"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Åbn indstillinger for notifikationer"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikationer er sat på pause af Forstyr ikke"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen notifikationer}=1{Notifikationer er sat på pause af {mode}}=2{Notifikationer er sat på pause af {mode} og én anden tilstand}one{Notifikationer er sat på pause af {mode} og # anden tilstand}other{Notifikationer er sat på pause af {mode} og # andre tilstande}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hovedregistrering"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringetilstand"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tryk for at ændre ringetilstand"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skærm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Skriv en note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Brug opdelt skærm med aktuel app til højre"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Brug opdelt skærm med aktuel app til venstre"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skift fra opdelt skærm til fuld skærm"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<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>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flyt det aktive vindue fra skærm til skærm"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flyt vinduet til venstre"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flyt vinduet til højre"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimér vinduet"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimer vinduet"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skift til næste sprog"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skift til forrige sprog"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemstyring"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Seneste apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Opdelt skærm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpas tastaturgenveje"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Skal genvejen fjernes?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du nulstille til standard?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryk på en tast for at tildele genvej"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Denne handling sletter din tilpassede genvej permanent."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Denne handling sletter alle dine tilpassede genveje permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon for handlingstast eller metatast"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpas"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nulstil"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Udfør"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"skråstreg"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Konfigurer genvej"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, nulstil"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuller"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryk på en tast"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinationen er allerede i brug. Prøv en anden tast."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Flot klaret!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har udført bevægelsen for at se alle apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation med vejledning. Klik for at sætte afspilningen på pause og genoptage den."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4994130ca5e4..bf323ddbb2f6 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Wenn du die Funktion „Widgets auf dem Sperrbildschirm“ als Verknüpfung hinzufügen möchtest, musst du diese Funktion zuerst in den Einstellungen aktivieren."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Zum Hinzufügen der Verknüpfung „Widgets“ musst du zuerst in den Einstellungen die Option „Widgets auf Sperrbildschirm zeigen“ aktivieren."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Einstellungen"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Unterhaltungen"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle lautlosen Benachrichtigungen löschen"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Benachrichtigungseinstellungen öffnen"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Benachrichtigungen durch „Bitte nicht stören“ pausiert"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Keine Benachrichtigungen}=1{Benachrichtigungen durch {mode} pausiert}=2{Benachrichtigungen durch {mode} und einen weiteren Modus pausiert}other{Benachrichtigungen durch {mode} und # weitere Modi pausiert}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Erfassung von Kopfbewe­gungen"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"Klingeltonmodus"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, zum Ändern des Klingeltonmodus tippen"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bildschirm sperren"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Notiz machen"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Splitscreen mit der aktuellen App auf der rechten Seite nutzen"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Splitscreen mit der aktuellen App auf der linken Seite nutzen"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Vom Splitscreen zum Vollbild wechseln"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus zu einer App rechts oder unten wechseln"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus zu einer App links oder oben wechseln"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Im Splitscreen: eine App durch eine andere ersetzen"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktives Fenster auf anderes Display verschieben"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Fenster nach links verschieben"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Fenster nach rechts verschieben"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Fenster maximieren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Fenster minimieren"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Eingabe"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Zur nächsten Sprache wechseln"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Zur vorherigen Sprache wechseln"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System­steuerelemente"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System-Apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Zuletzt verwendete Apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüp­fungen"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastenkombination entfernen?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Auf Standardeinstellung zurücksetzen?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Drücke eine Taste, um eine Tastenkombination festzulegen"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dadurch wird die benutzerdefinierte Tastenkombination endgültig gelöscht."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dadurch werden alle deine benutzerdefinierten Tastenkombinationen endgültig gelöscht."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Symbol für Aktions- oder Meta-Taste"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussymbol"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassen"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Zurücksetzen"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fertig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"Schrägstrich"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tastenkombination festlegen"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Entfernen"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, zurücksetzen"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Taste drücken"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Diese Tastenkombination wird bereits verwendet. Versuche es mit einer anderen Taste."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Perfekt!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du hast das Tutorial für die Touch-Geste zum Aufrufen aller Apps abgeschlossen"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation während des Tutorials, zum Pausieren und Fortsetzen der Wiedergabe klicken."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1e4ef8564d7e..a90f70122a2c 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Για να προσθέσετε το Widgets στην οθόνη κλειδώματος ως συντόμευση, βεβαιωθείτε ότι είναι ενεργοποιημένο στις ρυθμίσεις."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Για να προσθέσετε τη συντόμευση Γραφικά στοιχεία, βεβαιωθείτε ότι η ρύθμιση Εμφάνιση γραφικών στοιχείων στην οθόνη κλειδώματος είναι ενεργοποιημένη στις ρυθμίσεις."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ρυθμίσεις"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Εμφάνιση κουμπιού προφύλαξης οθόνης"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Συζητήσεις"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Διαγραφή όλων των ειδοποιήσεων σε σίγαση"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Άνοιγμα ρυθμίσεων ειδοποιήσεων"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία \"Μην ενοχλείτε\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Δεν υπάρχουν ειδοποιήσεις}=1{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode}}=2{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και μία άλλη λειτουργία}other{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και # άλλες λειτουργίες}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"λειτουργία ειδοποίησης ήχου"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, πατήστε για αλλαγή της λειτουργίας ειδοποίησης ήχου"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Κλείδωμα οθόνης"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Δημιουργία σημείωσης"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Πολυδιεργασία"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Χρήση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα δεξιά"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Χρήση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα αριστερά"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Εναλλαγή από διαχωρισμό οθόνης σε πλήρη οθόνη"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Εναλλαγή στην εφαρμογή δεξιά ή κάτω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Κατά τον διαχωρισμό οθόνης: αντικατάσταση μιας εφαρμογής με άλλη"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Μετακίνηση ενεργού παραθύρου μεταξύ οθονών"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Μετακίνηση παραθύρου αριστερά"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Μετακίνηση παραθύρου δεξιά"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Μεγιστοποίηση παραθύρου"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ελαχιστοποίηση παραθύρου"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Είσοδος"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Εναλλαγή στην επόμενη γλώσσα"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Εναλλαγή στην προηγούμενη γλώσσα"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Στοιχεία ελέγχου συστήματος"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Εφαρμογές συστήματος"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Πρόσφατες εφαρμογές"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Διαχωρισμός οθόνης"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Εισαγωγή"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Προσαρμογή συντομεύσεων πληκτρολογίου"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Κατάργηση συντόμευσης;"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Επαναφορά στις προεπιλογές;"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Πατήστε το πλήκτρο για ανάθεση της συντόμευσης"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Με αυτή την ενέργεια, η προσαρμοσμένη συντόμευση θα διαγραφεί οριστικά."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Με αυτή την ενέργεια θα διαγραφούν οριστικά όλες οι προσαρμοσμένες συντομεύσεις."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Εικονίδιο πλήκτρου ενέργειας ή Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Εικονίδιο συν"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Προσαρμογή"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Επαναφορά"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Τέλος"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"συν"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"κάθετος"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ορισμός συντόμευσης"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Κατάργηση"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ναι, επαναφορά"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ακύρωση"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Πατήστε ένα πλήκτρο"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ο συνδυασμός πλήκτρων χρησιμοποιείται ήδη. Δοκιμάστε άλλο πλήκτρο."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Μπράβο!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ολοκληρώσατε την κίνηση για την προβολή όλων των εφαρμογών"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Κινούμενη εικόνα οδηγού, κάντε κλικ για παύση και συνέχιση της αναπαραγωγής."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 938caa14f945..679684a31ee7 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index dada5e992f14..f54745dfc3a5 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head Tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -868,9 +872,9 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+ <string name="system_multitasking_rhs" msgid="8779289852395243004">"Use split screen with app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="7348595296208696452">"Use split screen with app on the left"</string>
+ <string name="system_multitasking_full_screen" msgid="4940465971687159429">"Switch to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: replace an app from one to another"</string>
@@ -1415,7 +1419,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1424,14 +1427,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customize keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customize"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
@@ -1441,6 +1447,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1472,6 +1479,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 938caa14f945..679684a31ee7 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 938caa14f945..679684a31ee7 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 5389aba2c2ac..ca53976cd3fb 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -450,7 +450,7 @@
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
- <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sí • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
<string name="zen_mode_set_up" msgid="8231201163894922821">"Sin establecer"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrar en configuración"</string>
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para agregar Widgets en la pantalla de bloqueo como combinación de teclas, asegúrate de que la función esté habilitada en la configuración."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para agregar el acceso directo de \"Widgets\", asegúrate de que la opción \"Mostrar widgets en la pantalla de bloqueo\" esté habilitada en la configuración."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botón para mostrar el protector de pantalla"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configuración de notificaciones"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo \"No interrumpir\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{{mode} pausó las notificaciones}=2{{mode} y un modo más pausaron las notificaciones}many{{mode} y # de modos más pausaron las notificaciones}other{{mode} y # modos más pausaron las notificaciones}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Monitoreo de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, presiona para cambiar el modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloquear la pantalla"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crear una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Tareas múltiples"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la app actual a la derecha"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la app actual a la izquierda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubicar la app a la derecha o abajo cuando usas la pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubicar la app a la izquierda o arriba cuando usas la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante pantalla dividida: Reemplaza una app con otra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover la ventana activa de una pantalla a otra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover la ventana hacia la izquierda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover la ventana hacia la derecha"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventana"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventana"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar al próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar al idioma anterior"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recientes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar el acceso directo?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Quieres restablecer la configuración predeterminada?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Presiona la tecla para asignar el acceso directo"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu acceso directo personalizado de forma permanente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta acción borrará todos tus accesos directos personalizados de forma permanente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícono tecla meta o de acción"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ícono de signo más"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Listo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"más"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra diagonal"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Presiona una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinación de teclas ya está en uso. Prueba con otra."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Bien hecho!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaste el gesto para ver todas las apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación del instructivo. Haz clic para pausar y reanudar la reproducción."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 2ff38b02e531..28812cca3c43 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartir audio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Permite compartir audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Admite Compartir audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para añadir la función de widgets en la pantalla de bloqueo como combinación de teclas, asegúrate de que la opción esté habilitada en los ajustes."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para añadir el acceso directo Widgets, asegúrate de que la opción Mostrar widgets en la pantalla de bloqueo esté habilitada en los ajustes."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ajustes"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir los ajustes de notificaciones"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo No molestar"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{Notificaciones pausadas por {mode}}=2{Notificaciones pausadas por {mode} y un modo más}many{Notificaciones pausadas por {mode} y # modos más}other{Notificaciones pausadas por {mode} y # modos más}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimiento de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toca para cambiar el modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Escribir una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarea"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la aplicación actual a la derecha"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la aplicación actual a la izquierda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la aplicación de la derecha o de abajo en pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover ventana activa de una pantalla a otra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover ventana a la izquierda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover ventana a la derecha"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventana"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventana"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar a siguiente idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar a idioma anterior"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicaciones del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicaciones recientes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar las combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Eliminar combinación de teclas?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Restablecer valores predeterminados?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pulsa una tecla para asignar una combinación de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Se eliminará tu combinación de teclas personalizada de forma permanente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Se eliminarán todos tus accesos directos personalizados de forma permanente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icono de la tecla de acción o de la tecla Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icono de más"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hecho"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"más"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra inclinada"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ajustes del teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eliminar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pulsa una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinación de teclas ya se está usando. Prueba con otra tecla."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Muy bien!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completado el gesto para ver todas las aplicaciones"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación del tutorial, haz clic para pausar y reanudar la reproducción."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index f646e258535f..2c0c378e34ed 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidinad"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Selleks et lisada vidinad otseteena lukustuskuvale, veenduge, et see oleks seadetes lubatud."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Otsetee „Vidinad“ lisamiseks veenduge, et seadetes oleks valik „Kuva lukustuskuval vidinad“ lubatud."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Seaded"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Vestlused"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kustuta kõik hääletud märguanded"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Avage märguandeseaded"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Režiim Mitte segada peatas märguanded"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Märguandeid pole}=1{{mode} peatas märguanded}=2{{mode} ja veel üks režiim peatasid märguanded}other{{mode} ja veel # režiimi peatasid märguanded}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pea jälgimine"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"telefonihelina režiim"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, puudutage telefonihelina režiimi muutmiseks"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukustuskuva"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Märkme tegemine"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitegumtöö"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse paremal"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse vasakul"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Jagatud ekraanikuvalt täisekraanile lülitamine"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Paremale või alumisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekraanikuva jagamise ajal: ühe rakenduse asendamine teisega"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktiivse akna teisaldamine ekraanide vahel"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Akna vasakule liigutamine"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Akna paremale liigutamine"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Akna maksimeerimine"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Akna minimeerimine"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sisend"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Järgmisele keelele lülitamine"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Eelmisele keelele lülitamine"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Süsteemi juhtelemendid"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Süsteemirakendused"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitegumtöö"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Hiljutised rakendused"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jagatud ekraanikuva"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatuuri otseteede kohandamine"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kas soovite otsetee eemaldada?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Kas lähtestada vaikeseadele?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Otsetee lisamiseks vajutage klahvi"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"See kustutab teie kohandatud otsetee jäädavalt."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"See kustutab kõik teie kohandatud otseteed jäädavalt."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiming või metaklahv"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluss-ikoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Lähtesta"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"pluss"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kaldkriips"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määrake otsetee"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eemalda"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jah, lähtesta"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klahvikombinatsioon on juba kasutusel. Proovige mõnda muud klahvi."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hästi tehtud!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tegite kõigi rakenduste vaatamise liigutuse"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Õpetlik animatsioon, klõpsake esitamise peatamiseks ja jätkamiseks."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 7d3c733ce58d..cfd6c6a5e495 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetak"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pantaila blokeatuan lasterbide gisa widgetak gehitzeko, ziurtatu aukera hori gaituta dagoela ezarpenetan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widgetak\" lasterbidea gehitzeko, ziurtatu \"Erakutsi widgetak pantaila blokeatuan\" gaituta dagoela ezarpenetan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ezarpenak"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Elkarrizketak"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Garbitu soinurik gabeko jakinarazpen guztiak"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ireki jakinarazpen-ezarpenak"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ez molestatzeko moduak pausatu egin ditu jakinarazpenak"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ez dago jakinarazpenik}=1{Modu honek jakinarazpenak pausatu ditu: {mode}}=2{Modu honek eta beste modu batek jakinarazpenak pausatu dituzte: {mode}}other{Modu honek eta beste # moduk jakinarazpenak pausatu dituzte: {mode}}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Buruaren jarraipena"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"tonu-jotzailearen modua"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Sakatu tonu-jotzailearen modua aldatzeko."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blokeatu pantaila"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Egin ohar bat"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Erabili pantaila zatitua eta ezarri aplikazio hau eskuinean"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Erabili pantaila zatitua eta ezarri aplikazio hau ezkerrean"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Aldatu pantaila zatitutik pantaila osora"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Aldatu eskuineko edo beheko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pantaila zatituan zaudela, ordeztu aplikazio bat beste batekin"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Eraman leiho aktiboa pantaila batetik bestera"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Eraman leihoa ezkerrera"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Eraman leihoa eskuinera"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizatu leihoa"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizatu leihoa"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sarrera"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Aldatu hurrengo hizkuntzara"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Aldatu aurreko hizkuntzara"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistema kontrolatzeko aukerak"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemaren aplikazioak"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Azkenaldiko aplikazioak"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantaila zatitzea"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pertsonalizatu lasterbideak"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Lasterbidea kendu nahi duzu?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Balio lehenetsia berrezarri nahi duzu?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Sakatu tekla lasterbidea esleitzeko"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Betiko ezabatuko da lasterbide pertsonalizatua."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lasterbide pertsonalizatu guztiak betiko ezabatuko dira."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ekintzaren edo Meta teklaren ikonoa"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus-ikonoa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pertsonalizatu"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Berrezarri"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Eginda"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"gehi"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ezarri lasterbidea"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kendu"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bai, berrezarri"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Utzi"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Sakatu tekla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tekla-konbinazio hori erabili da dagoeneko. Probatu beste tekla bat."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bikain!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Osatu duzu aplikazio guztiak ikusteko keinua"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorialeko animazioa. Sakatu pausatzeko eta erreproduzitzeari berrekiteko."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 153a460022a8..163d84bd8239 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ابزاره‌ها"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"برای افزودن «ابزاره‌ها» در صفحه قفل به‌عنوان میان‌بر، مطمئن شوید این گزینه در تنظیمات فعال باشد."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"برای افزودن میان‌بر «ابزاره‌ها»، مطمئن شوید «نمایش ابزاره‌ها در صفحه قفل» در تنظیمات فعال باشد."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"تنظیمات"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اعلان‌ها"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"مکالمه‌ها"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"پاک کردن همه اعلان‌های بی‌صدا"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"باز کردن تنظیمات اعلان"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"اعلان‌ها توسط «مزاحم نشوید» موقتاً متوقف شدند"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{اعلانی موقتاً متوقف نشده است}=1{اعلان‌ها را «{mode}» موقتاً متوقف کرده است}=2{اعلان‌ها را «{mode}» و یک حالت دیگر موقتاً متوقف کرد‌اند}one{اعلان‌ها را «{mode}» و # حالت دیگر موقتاً متوقف کرده‌اند}other{اعلان‌ها را «{mode}» و # حالت دیگر موقتاً متوقف کرده‌اند}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ردیابی سر"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، تک‌ضرب بزنید"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"حالت زنگ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>، برای تغییر حالت زنگ تک‌ضرب بزنید"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"قفل صفحه"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"یادداشت‌برداری"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"چندوظیفگی"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"استفاده از صفحهٔ دونیمه با برنامه فعلی در سمت راست"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"استفاده از صفحهٔ دونیمه با برنامه فعلی در سمت چپ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"جابه‌جایی از صفحهٔ دونیمه به تمام صفحه"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"رفتن به برنامه سمت راست یا پایین درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"درحین صفحهٔ دونیمه: برنامه‌ای را با دیگری جابه‌جا می‌کند"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"جابه‌جا کردن پنجره فعال بین نمایشگرها"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"بردن پنجره به چپ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"بردن پنجره به راست"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"به‌حداکثر رساندن اندازه پنجره"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"به‌حداقل رساندن اندازه پنجره"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ورودی"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"رفتن به زبان بعدی"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"رفتن به زبان قبلی"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"کنترل‌های سیستم"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"برنامه‌های سیستم"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"برنامه‌های اخیر"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"صفحهٔ دونیمه"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ورودی"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"میان‌برهای برنامه"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"میان‌برهای صفحه‌کلید"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"سفارشی‌سازی کردن میان‌برهای صفحه‌کلید"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"میان‌بر حذف شود؟"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"به تنظیم پیش‌فرض بازنشانی می‌کنید؟"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"برای اختصاص دادن میان‌بر، کلید را فشار دهید"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"با این کار، میان‌بر سفارشی شما برای همیشه حذف می‌شود."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"با این کار، همه میان‌برهای سفارشی برای همیشه حذف خواهند شد."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجه‌ای برای جستجو پیدا نشد"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"نماد کلید کنش یا متا"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"نماد جمع"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"سفارشی‌سازی کردن"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"بازنشانی"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تمام"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"و"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"کج‌خط روبه جلو"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحه‌کلید"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"تنظیم میان‌بر"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"حذف"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"بله، بازنشانی شود"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"لغو"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید را فشار دهید"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ترکیب کلید ازقبل درحال استفاده است. کلید دیگری را امتحان کنید."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"عالی بود!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"اشاره «مشاهده همه برنامه‌ها» را تمام کردید"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"پویانمایی آموزش گام‌به‌گام، برای توقف موقت و ازسرگیری پخش کلیک کنید."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 76d66c525256..1caac76c623e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetit"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Jos haluat lisätä Widgetit lukitusnäytöllä ‑ominaisuuden pikakuvakkeeksi, varmista, että se on otettu käyttöön asetuksissa."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jos haluat lisätä Widgetit-pikakuvakkeen, varmista, että \"Näytä widgetit lukitusnäytöllä\" on käytössä asetuksissa."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Asetukset"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Keskustelut"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Tyhjennä kaikki hiljaiset ilmoitukset"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Avaa ilmoitusasetukset"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Älä häiritse ‑tila keskeytti ilmoitukset"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ei ilmoituksia}=1{{mode} keskeytti ilmoitukset}=2{{mode} ja yksi muu tila keskeytti ilmoitukset}other{{mode} ja # muuta tilaa keskeytti ilmoitukset}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pään seuranta"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"Soittoäänen tila"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, vaihda soittoäänen tilaa napauttamalla"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukitse näyttö"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Tee muistiinpano"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitaskaus"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on oikealla"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on vasemmalla"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Vaihda jaetusta näytöstä koko näyttöön"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetussa näytössä"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetussa näytössä"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Jaetun näytön aikana: korvaa sovellus toisella"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Siirrä aktiivinen ikkuna näytöltä toiselle"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Siirrä ikkuna vasemmalle"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Siirrä ikkuna oikealle"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Suurenna ikkuna"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pienennä ikkuna"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Syöttötapa"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Vaihda seuraavaan kieleen"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Vaihda aiempaan kieleen"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Järjestelmän hallinta"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Järjestelmäsovellukset"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Viimeisimmät sovellukset"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jaettu näyttö"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pikanäppäimien muokkaaminen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Poistetaanko pikanäppäin?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Palautetaanko oletusasetukset?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Määritä pikanäppäin painamalla näppäintä"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Oma pikanäppäin poistetaan pysyvästi."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kaikki omat pikanäppäimet poistetaan pysyvästi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiminto- tai Meta-näppäinkuvake"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluskuvake"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Muokkaa"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nollaa"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kauttaviiva"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Näppäimistön asetukset"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Valitse pikanäppäin"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Poista"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Kyllä, nollaa"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Peru"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paina näppäintä"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Näppäinyhdistelmä on jo käytössä. Kokeile toista näppäintä."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ohjeanimaatio, klikkaa keskeyttääksesi ja jatkaaksesi."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6997b26b7c12..6d56ac1abf26 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pour ajouter des widgets comme raccourci à l\'écran de verrouillage, assurez-vous que cette fonctionnalité soit activée dans les paramètres."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci « Widgets », assurez-vous que « Afficher les widgets sur l\'écran de verrouillage » est activé dans les paramètres."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applis et les données de cette session seront supprimées."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ouvrir les paramètres des notifications"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Les notifications sont suspendues par le mode Ne pas déranger"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par {mode}}=2{Notifications suspendues par {mode} et un autre mode}one{Notifications suspendues par {mode} et # autre mode}many{Notifications suspendues par {mode} et # d\'autres modes}other{Notifications suspendues par {mode} et # autres modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toucher ici pour modifier le mode de la sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Prendre une note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitâche"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'Écran divisé avec l\'appli actuelle à droite"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'Écran divisé avec l\'appli actuelle à gauche"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'Écran divisé au plein écran"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'Écran divisé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Déplacer la fenêtre active d\'un écran à l\'autre"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Déplacer la fenêtre vers la gauche"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Déplacer la fenêtre vers la droite"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Agrandir la fenêtre"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Réduire la fenêtre"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrée"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passer à la langue précédente"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Commandes système"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Applis récentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran divisé"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis-clavier"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Réinitialiser aux raccourcis par défaut?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur la touche pour attribuer un raccourci"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Cela supprimera définitivement votre raccourci personnalisé."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Cette action supprimera définitivement tous vos raccourcis personnalisés."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de la touche Action ou Méta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Réinitialiser"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Terminé"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barre oblique"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, réinitialiser"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinaison de touches est déjà utilisée. Essayez une autre touche."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation du tutoriel; cliquer ici pour mettre en pause et reprendre la lecture."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3e7fc389d5c2..fd9854b7b235 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pour ajouter des widgets à l\'écran de verrouillage en tant que raccourcis, assurez-vous que cette option est activée dans les paramètres."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci \"Widgets\", assurez-vous que l\'option \"Afficher les widgets sur l\'écran de verrouillage\" est activée dans les paramètres."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ouvrir les paramètres de notification"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications suspendues par le mode Ne pas déranger"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par le mode {mode}}=2{Notifications suspendues par le mode {mode} et un autre mode}one{Notifications suspendues par le mode {mode} et # autre mode}many{Notifications suspendues par le mode {mode} et # d\'autres modes}other{Notifications suspendues par le mode {mode} et # autres modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, appuyez pour changer le mode de la sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Créer une note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitâche"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'écran partagé avec l\'appli actuelle sur la droite"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'écran partagé avec l\'appli actuelle sur la gauche"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran partagé au plein écran"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'écran partagé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode écran partagé : Remplacer une appli par une autre"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Déplacer la fenêtre active d\'un écran à l\'autre"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Déplacer la fenêtre vers la gauche"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Déplacer la fenêtre vers la droite"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Agrandir la fenêtre"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Réduire la fenêtre"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Saisie"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Revenir à la langue précédente"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Commandes système"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Applis récentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran partagé"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis clavier"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Rétablir les paramètres par défaut ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur une touche pour attribuer un raccourci"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Votre raccourci personnalisé sera définitivement supprimé."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tous vos raccourcis personnalisés seront définitivement supprimés."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de touche d\'action ou de méta-touche"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Réinitialiser"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"OK"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barre oblique"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, rétablir"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinaison de touches déjà utilisée. Essayez une autre touche."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation du tutoriel, cliquez pour mettre en pause et reprendre la lecture."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d36a24dab815..96b858b62a7f 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para poderes engadir widgets á pantalla de bloqueo como atallos, debe estar activada na configuración a opción correspondente."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para engadir o atallo Widgets, vai a Configuración e comproba que está activada a opción Mostrar widgets na pantalla de bloqueo."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas as notificacións silenciadas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir a configuración de notificacións"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"O modo Non molestar puxo en pausa as notificacións"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Non hai ningunha notificación}=1{Notificacións postas en pausa polo modo {mode}}=2{Notificacións postas en pausa polo modo {mode} e un máis}other{Notificacións postas en pausa polo modo {mode} e # máis}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimento da cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toca para cambiar o modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crear nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefa"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar pantalla dividida coa aplicación actual na dereita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar pantalla dividida coa aplicación actual na esquerda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar á aplicación da dereita ou de abaixo coa pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En modo de pantalla dividida: Substituír unha aplicación por outra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover ventá activa entre pantallas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover ventá á esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover ventá á dereita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventá"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventá"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar ao seguinte idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar ao idioma anterior"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controis do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacións do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicacións recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar os atallos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Queres quitar o atallo?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Queres restablecer a opción predeterminada?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Preme a tecla para asignar o atallo"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Eliminarase de forma permanente o teu atallo personalizado."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Eliminaranse permanentemente todos os teus atallos personalizados."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona da tecla Meta ou de acción"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona do signo máis"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Feito"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"máis"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra oblicua"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atallo"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Si, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Preme unha tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Xa se está usando esta combinación de teclas. Proba con outra."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben feito!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaches o titorial do xesto de ver todas as aplicacións"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación do titorial, fai clic para poñelo en pausa ou retomar a reprodución."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 1c528682e413..fb494c1dd7dd 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"વિજેટ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"લૉક સ્ક્રીન પર શૉર્ટકટ તરીકે વિજેટ ઉમેરવા માટે, ખાતરી કરો કે તે સેટિંગમાં ચાલુ કરેલા હોય."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"વિજેટ\"નો શૉર્ટકટ ઉમેરવા માટે, ખાતરી કરો કે સેટિંગમાં \"લૉક સ્ક્રીન પર વિજેટ બતાવો\" સુવિધા ચાલુ કરેલી છે."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"સેટિંગ"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"નોટિફિકેશન"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"વાતચીત"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"બધા સાઇલન્ટ નોટિફિકેશન સાફ કરો"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"નોટિફિકેશનના સેટિંગ ખોલો"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{કોઈ નોટિફિકેશન નથી}=1{{mode} દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}=2{{mode} અને અન્ય એક મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}one{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}other{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"હૅડ ટ્રૅકિંગ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"લૉક સ્ક્રીન"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"નોંધ લો"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"એકસાથે એકથી વધુ કાર્યો કરવા"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"જમણી બાજુએ વર્તમાન ઍપ સાથે વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ડાબી બાજુએ વર્તમાન ઍપ સાથે વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"વિભાજિત સ્ક્રીનથી પૂર્ણ સ્ક્રીન પર સ્વિચ કરો"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે જમણી બાજુ કે નીચેની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"વિભાજિત સ્ક્રીન દરમિયાન: એક ઍપને બીજી ઍપમાં બદલો"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"સક્રિય વિન્ડોને ડિસ્પ્લેની વચ્ચે ખસેડો"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"વિંડોને ડાબી બાજુ ખસેડો"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"વિંડોને જમણી બાજુ ખસેડો"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"વિંડો મોટી કરો"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"વિંડો નાની કરો"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ઇનપુટ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"આગલી ભાષા પર સ્વિચ કરો"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"પાછલી ભાષા પર સ્વિચ કરો"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"સિસ્ટમના નિયંત્રણો"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"સિસ્ટમ ઍપ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"તાજેતરની ઍપ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ઇનપુટ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ઍપ શૉર્ટકટ"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"કીબોર્ડ શૉર્ટકટને કસ્ટમાઇઝ કરો"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"શું શૉર્ટકટ કાઢી નાખીએ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"પાછા ડિફૉલ્ટ પર રીસેટ કરીએ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"શૉર્ટકટ સોંપવા માટે કી દબાવો"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"આ તમારા કસ્ટમ શૉર્ટકટને કાયમી રીતે ડિલીટ કરશે."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"આ તમારા બધા કસ્ટમ શૉર્ટકટને કાયમ માટે ડિલીટ કરશે."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ઍક્શન અથવા મેટા કીનું આઇકન"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"પ્લસનું આઇકન"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"કસ્ટમાઇઝ કરો"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"રીસેટ કરો"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"થઈ ગયું"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"વત્તા"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ફૉરવર્ડ સ્લૅશ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"શૉર્ટકટ સેટ કરો"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"કાઢી નાખો"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"હા, રીસેટ કરો"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"રદ કરો"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"કી દબાવો"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"કી સંયોજન પેહલેથી ઉપયોગમાં છે. અન્ય કી અજમાવી જુઓ."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"વાહ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"તમે \'બધી ઍપ જુઓ\' સંકેત પૂર્ણ કર્યો"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ટ્યૂટૉરિઅલ ઍનિમેશન થોભાવવાનું અને ચલાવવાનું ફરી શરૂ કરવા માટે ક્લિક કરો."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index fafb045c5b45..c84a3eb936bb 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -450,7 +450,7 @@
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"चालू है"</string>
- <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • पर"</string>
+ <string name="zen_mode_on_with_details" msgid="7416143430557895497">"चालू है • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"बंद है"</string>
<string name="zen_mode_set_up" msgid="8231201163894922821">"सेट नहीं है"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग में जाकर मैनेज करें"</string>
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"लॉक स्क्रीन पर शॉर्टकट के तौर पर विजेट जोड़ने के लिए, पक्का करें कि सेटिंग में यह सुविधा चालू हो."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोड़ने के लिए, पक्का करें कि सेटिंग में \"लॉक स्क्रीन पर विजेट दिखाएं\" चालू हो."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रीन सेवर दिखाने का बटन"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"सूचना सेटिंग खोलें"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोई सूचना नहीं है}=1{{mode} की वजह से सूचना नहीं दिख रही है}=2{{mode} और एक अन्य मोड की वजह से सूचना नहीं दिख रही है}one{{mode} और # अन्य मोड की वजह से सूचना नहीं दिख रही है}other{{mode} और # अन्य मोड के की वजह से सूचनाएं नहीं दिख रही है}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग चालू है"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, रिंगर मोड बदलने के लिए टैप करें"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रीन लॉक करने के लिए"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"नोट बनाने के लिए"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"स्प्लिट स्क्रीन में मौजूदा ऐप्लिकेशन को दाईं ओर दिखाने के लिए"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"स्प्लिट स्क्रीन में मौजूदा ऐप्लिकेशन को बाईं ओर दिखाने के लिए"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीन से फ़ुल स्क्रीन मोड पर स्विच करने के लिए"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन पर, दाईं ओर या नीचे के ऐप पर स्विच करने के लिए"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीन के दौरान: एक ऐप्लिकेशन को दूसरे ऐप्लिकेशन से बदलें"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ऐक्टिव विंडो को एक से दूसरे डिसप्ले पर स्विच करें"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"विंडो को बाईं ओर ले जाएं"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"विंडो को दाईं ओर ले जाएं"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"विंडो को बड़ा करें"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"विंडो को छोटा करें"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"अगली भाषा पर स्विच करने के लिए"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"पिछली भाषा पर स्विच करने के लिए"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्टम से जुड़े कंट्रोल"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम के ऐप्लिकेशन"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ऐप शॉर्टकट"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट को पसंद के मुताबिक बनाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"क्या आपको शॉर्टकट हटाना है?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"क्या आपको फिर से डिफ़ॉल्ट सेटिंग चालू करनी है?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करने के लिए बटन दबाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने से, आपका कस्टम शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ऐसा करने पर, आपके सभी कस्टम शॉर्टकट हमेशा के लिए मिट जाएंगे."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शॉर्टकट खोजें"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ऐक्शन या मेटा बटन का आइकॉन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस का आइकॉन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"पसंद के मुताबिक बनाएं"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रीसेट करें"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"हो गया"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"प्लस"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"फ़ॉरवर्ड स्लैश"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करें"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाएं"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"हां, रीसेट करें"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करें"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"बटन दबाएं"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"बटन का यह कॉम्बिनेशन पहले से इस्तेमाल किया जा रहा है. कोई दूसरा कॉम्बिनेशन आज़माएं."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"बहुत खूब!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"अब आपको हाथ के जेस्चर का इस्तेमाल करके, सभी ऐप्लिकेशन देखने का तरीका पता चल गया है"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्यूटोरियल ऐनिमेशन को रोकने और इन्हें फिर से चलाने के लिए क्लिक करें."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d1be021d49e0..6eabb6a2a9c9 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da biste dodali widgete na zaključani zaslon kao prečac, provjerite jesu li omogućeni u postavkama."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Prikaži gumb čuvara zaslona"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Izbriši sve bešumne obavijesti"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvori postavke obavijesti"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavijesti}=1{Obavijesti je pauzirao način {mode}}=2{Obavijesti su pauzirali način {mode} i još jedan način}one{Obavijesti su pauzirali način {mode} i još # način rada}few{Obavijesti su pauzirali način {mode} i još # načina rada}other{Obavijesti su pauzirali način {mode} i još # načina rada}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način softvera zvona"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dodirnite da biste promijenili način softvera zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -856,7 +860,7 @@
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Prikazuju se prečaci za trenutačnu aplikaciju"</string>
<string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaz obavijesti"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"Snimanje zaslona"</string>
- <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaži prečace"</string>
+ <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaz prečaca"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Natrag"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Otvaranje početnog zaslona"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaz nedavnih aplikacija"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje zaslona"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s desne strane"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s lijeve strane"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prelazak s podijeljenog zaslona na cijeli zaslon"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak na aplikaciju zdesna ili ispod uz podijeljeni zaslon"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prelazak na aplikaciju slijeva ili iznad uz podijeljeni zaslon"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijekom podijeljenog zaslona: zamijeni aplikaciju drugom"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premještanje aktivnog prozora između zaslona"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Premještanje prozora ulijevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Premještanje prozora udesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimiziranje prozora"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimiziranje prozora"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prelazak na sljedeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prelazak na prethodni jezik"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrole sustava"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacije sustava"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni zaslon"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodba tipkovnih prečaca"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite li ukloniti prečac?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite li vratiti na zadano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipku da biste dodijelili prečac"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Time će se vaš prilagođeni prečac trajno izbrisati."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time će se trajno izbrisati svi vaši prilagođeni prečaci."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke za radnju odnosno meta tipka"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Poništi"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavite prečac"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Odustani"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipki već se upotrebljava. Pokušajte s drugom tipkom."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Izvrsno!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Napravili ste pokret za prikaz svih aplikacija"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija u vodiču, kliknite za pauziranje i nastavak reprodukcije."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 4c5c36491359..2ee7b24fbe7f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Modulok"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ha szeretné gyorsparancsként hozzáadni a Modulok a lezárási képernyőn funkciót, győződjön meg arról, hogy a funkció engedélyezve van a beállításokban."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"A „Modulok” gyorsparancs hozzáadásához gondoskodjon arról, hogy a „Modulok megjelenítése a lezárási képernyőn” beállítás legyen engedélyezve a beállításokban."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Beállítások"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Beszélgetések"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Az összes néma értesítés törlése"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Értesítési beállítások megnyitása"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ne zavarjanak funkcióval szüneteltetett értesítések"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nincs értesítés}=1{A(z) {mode} szüneteltette az értesítéseket}=2{A(z) {mode} és egy másik mód szüneteltette az értesítéseket}other{A(z) {mode} és # másik mód szüneteltette az értesítéseket}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Fejkövetés"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"csengés módja"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Koppintson a csörgés módjának módosításához."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lezárási képernyő"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Jegyzetelés"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Osztott képernyő használata, a jelenlegi alkalmazás legyen jobb oldalt"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Osztott képernyő használata, a jelenlegi alkalmazás legyen bal oldalt"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Váltás osztott képernyőről teljes képernyőre"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Váltás a jobb oldalt, illetve lent lévő appra osztott képernyő esetén"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Váltás a bal oldalt, illetve fent lévő appra osztott képernyő esetén"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Osztott képernyőn: az egyik alkalmazás lecserélése egy másikra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktív ablak áthelyezése egyik kijelzőről a másikra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Ablak balra mozgatása"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Ablak mozgatása jobbra"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Ablak teljes méretre állítása"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ablak kis méretre állítása"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Bevitel"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Váltás a következő nyelvre"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Váltás az előző nyelvre"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Rendszervezérlők"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Rendszeralkalmazások"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Legutóbbi alkalmazások"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazásikonok"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"A billentyűparancsok személyre szabása"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Eltávolítja a billentyűparancsot?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Visszaállítja az alapértelmezett beállításokat?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nyomja meg a billentyűt a billentyűparancs hozzárendeléséhez"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ezzel véglegesen törli az egyéni billentyűparancsot."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ezzel véglegesen törli az összes egyéni billentyűparancsot."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Művelet vagy Meta billentyű ikonja"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Személyre szabás"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Visszaállítás"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kész"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plusz"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"törtvonal"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Billentyűparancs beállítása"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eltávolítás"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Igen, visszaállítom"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Mégse"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nyomja le a billentyűt"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A billentyűkombináció már használatban van. Próbálkozzon másik billentyűvel."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Szép munka!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Teljesítette az összes alkalmazás megtekintésének kézmozdulatát."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Útmutató animáció. Kattintson a szüneteltetéshez és a lejátszás folytatásához."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 065670098489..d43beb8b6407 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Վիջեթներ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Վիջեթները կողպէկրանին որպես դյուրանցում ավելացնելու համար համոզվեք, որ գործառույթը միացված է կարգավորումներում։"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"«Վիջեթներ» դյուրանցումն ավելացնելու համար համոզվեք, որ «Ցույց տալ վիջեթները կողպէկրանին» պարամետրը միացված է կարգավորումներում։"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Կարգավորումներ"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Զրույցներ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ջնջել բոլոր անձայն ծանուցումները"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Բացել ծանուցումների կարգավորումները"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ծանուցումներ չկան}=1{Ծանուցումները դադարեցվել են «{mode}» ռեժիմի կողմից}=2{Ծանուցումները դադարեցվել են «{mode}» ու ևս մի ռեժիմի կողմից}one{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}other{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Գլխի շարժումների հետագծում"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"զանգակի ռեժիմ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>․ հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Կողպէկրան"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ստեղծել նշում"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Բազմախնդրու­թյուն"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Տրոհել էկրանը և տեղավորել այս հավելվածը աջում"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Տրոհել էկրանը և տեղավորել այս հավելվածը ձախում"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Տրոհված էկրանից անցնել լիաէկրան ռեժիմ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Տրոհված էկրանի ռեժիմում մեկ հավելվածը փոխարինել մյուսով"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Տեղափոխել ակտիվ պատուհանը էկրանների միջև"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Պատուհանը տեղափոխել ձախ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Պատուհանը տեղափոխել աջ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Ծավալել պատուհանը"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ծալել պատուհանը"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ներածում"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Անցնել հաջորդ լեզվին"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Անցնել նախորդ լեզվին"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Համակարգի կառավարման տարրեր"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Համակարգային հավելվածներ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրու­թյուն"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Վերջին օգտագործած հավելվածները"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Տրոհված էկրան"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ներածում"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Հավելվածի դյուրանցումներ"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Կարգավորեք ստեղնային դյուրանցումներ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Հեռացնե՞լ դյուրանցումը"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Վերականգնե՞լ կանխադրվածները"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Սեղմեք որևէ ստեղն՝ դյուրանցում նշանակելու համար"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ձեր հատուկ դյուրանցումն ընդմիշտ կջնջվի։"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Բոլոր հատուկ դյուրանցումներն ընդմիշտ կջնջվեն։"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Գործողության կամ Meta ստեղնի պատկերակ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Պլյուս պատկերակ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Կարգավորել"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Զրոյացնել"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Պատրաստ է"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"գումարած"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"շեղ գիծ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ստեղծել դյուրանցում"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Հեռացնել"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Այո, վերականգնել"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք այլ ստեղն։"</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Հիանալի՛ է"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Դուք սովորեցիք բոլոր հավելվածները դիտելու ժեստը"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ուղեցույցի անիմացիա․ սեղմեք՝ նվագարկումը դադարեցնելու/վերսկսելու համար։"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index da121671232b..095b35bca77a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Untuk menambahkan Widget di layar kunci sebagai pintasan, pastikan Widget diaktifkan di setelan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tampilkan widget di layar kunci\" diaktifkan di setelan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setelan"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Percakapan"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hapus semua notifikasi senyap"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buka setelan notifikasi"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikasi dijeda oleh mode Jangan Ganggu"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tidak ada notifikasi}=1{Notifikasi dijeda oleh {mode}}=2{Notifikasi dijeda oleh {mode} dan satu mode lainnya}other{Notifikasi dijeda oleh {mode} dan # mode lainnya}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pelacakan Gerak Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode pendering"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ketuk untuk mengubah mode pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci layar"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Buat catatan"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kanan"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kiri"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih dari layar terpisah ke layar penuh"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Beralih ke aplikasi di bagian kanan atau bawah saat menggunakan layar terpisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Dalam layar terpisah: ganti salah satu aplikasi dengan yang lain"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Memindahkan jendela aktif dari satu layar ke layar lainnya"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Memindahkan jendela ke kiri"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Memindahkan jendela ke kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Memaksimalkan jendela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Meminimalkan jendela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih ke bahasa berikutnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih ke bahasa sebelumnya"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrol sistem"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikasi sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplikasi terbaru"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Layar terpisah"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Hapus pintasan?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset kembali ke pintasan default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan tombol untuk menetapkan pintasan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan menghapus pintasan kustom Anda secara permanen."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tindakan ini akan menghapus semua pintasan kustom Anda secara permanen."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon tombol Tindakan atau Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikon plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"garis miring"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setel pintasan"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hapus"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ya, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan tombol"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinasi tombol sudah digunakan. Coba tombol lain."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Oke!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah menyelesaikan gestur untuk melihat semua aplikasi"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animasi tutorial, klik untuk menjeda dan melanjutkan pemutaran."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index cee3ef4f6f5b..8a885f0efb77 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Græjur"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Til að bæta „Græjur á lásskjá“ við sem flýtileið skaltu ganga úr skugga um að kveikt sé á eiginleikanum í stillingunum."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Gakktu úr skugga um að kveikt sé á „Sýna græjur á lásskjá“ til að geta bætt flýtileiðinni „Græjur“ við."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Stillingar"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Hnappurinn „Sýna skjávara“"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtöl"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hreinsa allar þöglar tilkynningar"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Opna tilkynningastillingar"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Hlé gert á tilkynningum þar sem stillt er á „Ónáðið ekki“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Engar tilkynningar}=1{Hlé gert á tilkynningum þar sem stillt er á {mode}}=2{Hlé gert á tilkynningum þar sem stillt er á {mode} og eina aðra stillingu}one{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðra stillingu}other{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðrar stillingar}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rakning höfuðhreyfinga"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"hringistilling"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ýttu til að skipta um hringjarastillingu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lásskjár"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Skrifa glósu"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Fjölvinnsla"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Notaðu skjáskiptingu með núverandi forriti til hægri"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Notaðu skjáskiptingu með núverandi forriti til vinstri"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skipta úr skjáskiptingu yfir á allan skjáinn"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skiptu í forrit til hægri eða fyrir neðan þegar skjáskipting er notuð"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skiptu í forrit til vinstri eða fyrir ofan þegar skjáskipting er notuð"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Í skjáskiptingu: Skipta forriti út fyrir annað forrit"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kerfisstýringar"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Kerfisforrit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nýleg forrit"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Innsláttur"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sérsníddu flýtilykla"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Fjarlægja flýtileið?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Endurstilla á sjálfgefið?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Ýttu á lykil til að stilla flýtileið"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Þetta eyðir sérsniðnu flýtileiðinni varanlega."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Þetta verður til þess að öllum sérsniðnu flýtileiðunum þínum verður eytt varanlega."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leita að flýtileiðum"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Tákn lýsilykils (aðgerðarlykils)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plústákn"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sérsníða"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Endurstilla"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Lokið"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stilltu flýtileið"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjarlægja"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Já, endurstilla"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Hætta við"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Ýttu á lykil"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Lyklasamsetningin er þegar í notkun. Prófaðu annan lykil."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vel gert!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Þú framkvæmdir bendinguna „Sjá öll forrit“"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Leiðsagnarhreyfimynd, smelltu til að gera hlé og halda áfram að spila."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index bc6f262aa932..790a80dbc543 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Per aggiungere Widget alla schermata di blocco come scorciatoia, assicurati che sia abilitata nelle impostazioni."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per aggiungere la scorciatoia \"Widget\", assicurati che l\'opzione \"Mostra widget sulla schermata di blocco\" sia abilitata nelle impostazioni."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Impostazioni"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pulsante Mostra salvaschermo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversazioni"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Cancella tutte le notifiche silenziose"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Apri impostazioni di notifica"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifiche messe in pausa in base alla modalità Non disturbare"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nessuna notifica}=1{Notifica messa in pausa da {mode}}=2{Notifiche messe in pausa da {mode} e un\'altra modalità}many{Notifiche messe in pausa da {mode} e # di modalità}other{Notifiche messe in pausa da {mode} e altre # modalità}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rilev. movim. testa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modalità suoneria"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tocca per cambiare la modalità della suoneria"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
@@ -868,9 +872,9 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blocca lo schermo"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Scrivi una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilizza schermo diviso con l\'app corrente a destra"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilizza schermo diviso con l\'app corrente a sinistra"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passa da schermo diviso a schermo intero"</string>
+ <string name="system_multitasking_rhs" msgid="8779289852395243004">"Utilizza lo schermo diviso con l\'app a destra"</string>
+ <string name="system_multitasking_lhs" msgid="7348595296208696452">"Utilizza lo schermo diviso con l\'app a sinistra"</string>
+ <string name="system_multitasking_full_screen" msgid="4940465971687159429">"Passa alla modalità a schermo intero"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passa all\'app a destra o sotto mentre usi lo schermo diviso"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passa all\'app a sinistra o sopra mentre usi lo schermo diviso"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Con lo schermo diviso: sostituisci un\'app con un\'altra"</string>
@@ -1415,7 +1419,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controlli di sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"App di sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"App recenti"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Schermo diviso"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string>
@@ -1424,14 +1427,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizza scorciatoie da tastiera"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Rimuovere scorciatoia?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vuoi ripristinare il valore predefinito?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Premi un tasto per assegnare una scorciatoia"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"La scorciatoia personalizzata verrà eliminata definitivamente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tutte le tue scorciatoie personalizzate verranno eliminate definitivamente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona tasto Azione o Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona Più"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizza"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reimposta"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fine"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
@@ -1441,6 +1447,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Impostazioni tastiera"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Imposta scorciatoia"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Rimuovi"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sì, ripristina"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annulla"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Premi un tasto"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinazione di tasti già in uso. Prova con un altro tasto."</string>
@@ -1472,6 +1479,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben fatto!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hai completato il gesto Visualizza tutte le app."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animazione del tutorial: fai clic per mettere in pausa e riprendere la riproduzione."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31f3e4e469e9..99939a86bc39 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ווידג\'טים"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"כדי להוסיף את קיצור דרך \"ווידג\'טים במסך הנעילה\", צריך לוודא שהתכונה הזו מופעלת בהגדרות."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"כדי להוסיף את קיצור הדרך \"ווידג\'טים\", צריך לוודא שהאפשרות \"ווידג\'טים במסך הנעילה\" מופעלת בהגדרות."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"הגדרות"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"התראות"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ניקוי כל ההתראות השקטות"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"פתיחה של הגדרת ההתראות"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{אין התראות}=1{ההתראות הושהו על ידי {mode}}=2{ההתראות הושהו על ידי {mode} ועל ידי מצב אחד נוסף}one{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}other{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב ראש"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, צריך להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"נעילת המסך"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"כתיבת הערה"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ריבוי משימות"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"שימוש במסך מפוצל כשהאפליקציה הנוכחית בצד ימין"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"שימוש במסך מפוצל כשהאפליקציה הנוכחית בצד שמאל"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"החלפה ממסך מפוצל למסך מלא"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"מעבר לאפליקציה משמאל או למטה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"כשהמסך מפוצל: החלפה בין אפליקציה אחת לאחרת"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"העברת החלון הפעיל בין מסכים"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"העברת החלון שמאלה"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"העברת החלון ימינה"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"הגדלת החלון"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"מזעור החלון"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"קלט"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"מעבר לשפה הבאה"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"מעבר לשפה הקודמת"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"הגדרות המערכת"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"אפליקציות מערכת"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"אפליקציות שהיו בשימוש לאחרונה"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"מסך מפוצל"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"קיצורי דרך של אפליקציות"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"התאמה אישית של מקשי הקיצור"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"להסיר את קיצור הדרך?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"לאפס לברירת המחדל?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"צריך להקיש על מקש כדי להקצות מקש קיצור"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק באופן סופי."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"הפעולה הזו תמחק באופן סופי את כל קיצורי הדרך המותאמים אישית."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"סמל מקש הפעולה (\"מטא\")"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"סמל הפלוס"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"התאמה אישית"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"איפוס"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"סיום"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"סימן חיבור (פלוס)"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"קו נטוי"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"הגדרה של מקש קיצור"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"הסרה"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"כן, לאפס"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ביטול"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"יש ללחוץ על מקש"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"שילוב המקשים הזה כבר בשימוש. אפשר לנסות מקש אחר."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"כל הכבוד!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"סיימת לתרגל את התנועה להצגת כל האפליקציות"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"אנימציה של הדרכה, אפשר ללחוץ כדי להשהות ולהמשיך את ההפעלה."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d5fb1a783692..5fd029b25e5c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ウィジェット"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ロック画面にウィジェットをショートカットとして追加するには、設定でウィジェットが有効になっていることをご確認ください。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"[ウィジェット] ショートカットを追加するには、設定で [ロック画面でのウィジェットの表示] が有効になっていることを確認してください。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"スクリーンセーバー表示ボタン"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"会話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"サイレント通知がすべて消去されます"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"通知設定を開く"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"サイレント モードにより通知は一時停止中です"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{通知なし}=1{{mode} により通知は一時停止中です}=2{{mode} と他 1 個のモードにより通知は一時停止中です}other{{mode} と他 # 個のモードにより通知は一時停止中です}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘ⁠ッ⁠ド ト⁠ラ⁠ッ⁠キ⁠ン⁠グ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"着信音のモード"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>、タップすると、着信音のモードを変更できます"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"画面をロック"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"メモを入力する"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"マルチタスク"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"分割画面の使用(現在のアプリを右側に表示)"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"分割画面の使用(現在のアプリを左側に表示)"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"分割画面から全画面に切り替える"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"分割画面の使用時に右側または下部のアプリに切り替える"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"分割画面の使用時に左側または上部のアプリに切り替える"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"分割画面中: アプリを順に置換する"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"システム コントロール"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"システムアプリ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"マルチタスク"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使ったアプリ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割画面"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"入力"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"アプリのショートカット"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットをカスタマイズする"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ショートカットを削除しますか?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"デフォルトにリセットしますか?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ショートカットを割り当てるキーを押してください"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"この操作を行うと、カスタム ショートカットが完全に削除されます。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"この操作を行うと、すべてのカスタム ショートカットが完全に削除されます。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"アクションキーまたはメタキーのアイコン"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"プラスアイコン"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"カスタマイズ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"リセット"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完了"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ショートカットの設定"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"削除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"リセットする"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"キャンセル"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"キーを押してください"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"このキーの組み合わせはすでに使用されています。別のキーを試してください。"</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"キーボードのアクションキーを押します"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"完了です!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"「すべてのアプリを表示する」ジェスチャーを学習しました"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"チュートリアルのアニメーションです。クリックすると一時停止、もう一度クリックすると再開します。"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 8bbb1812c2cd..55d38c9ead8e 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ვიჯეტები"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ჩაკეტილ ეკრანზე ვიჯეტის მალსახმობის სახით დასამატებლად დარწმუნდით, რომ ის ჩართულია პარამეტრებიდან."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"„ვიჯეტების“ მალსახმობის დასამატებლად დარწმუნდით, რომ პარამეტრებში ჩართულია „დაბლოკილ ეკრანზე ვიჯეტების ჩვენება“."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"პარამეტრები"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ეკრანმზოგის ღილაკის ჩვენება"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"შეტყობინებები"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"საუბრები"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ყველა ჩუმი შეტყობინების გასუფთავება"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"შეტყობინების პარამეტრების გახსნა"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"შეტყობინებები დაპაუზდა „არ შემაწუხოთ“ რეჟიმის მეშვეობით"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{შეტყობინებები არ არის}=1{შეტყობინებები შეჩერებულია {mode}-ის გამო}=2{შეტყობინებები შეჩერებულია {mode}-ის და ერთი სხვა რეჟიმის გამო}other{შეტყობინებები შეჩერებულია {mode}-ის და # სხვა რეჟიმის გამო}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ხმის მიდევნებით"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"მრეკავის რეჟიმი"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ჩაკეტილი ეკრანი"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ჩაინიშნეთ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"მრავალამოცანიანი რეჟიმი"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ეკრანის გაყოფის გამოყენება მიმდინარე აპზე მარჯვნივ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ეკრანის გაყოფის გამოყენება მიმდინარე აპზე მარცხნივ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"გადართვა ეკრანის გაყოფიდან სრულ ეკრანზე"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ეკრანის გაყოფის გამოყენებისას აპზე მარჯვნივ ან ქვემოთ გადართვა"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ეკრანის გაყოფის გამოყენებისას აპზე მარცხნივ ან ზემოთ გადართვა"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ეკრანის გაყოფის დროს: ერთი აპის მეორით ჩანაცვლება"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"სისტემის მართვის საშუალებები"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"სისტემის აპები"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"მრავალამოცანიანი რეჟიმი"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ბოლოდროინდელი აპები"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ეკრანის გაყოფა"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"შეყვანა"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"აპის მალსახმობები"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"კლავიატურის მალსახმობების მორგება"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"გსურთ მალსახმობის წაშლა?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"გსურთ ნაგულისხმევზე გადაყენება?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"მალსახმობის მინიჭებისთვის დააჭირეთ კლავიშს"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ეს თქვენს მორგებულ მალსახმობებს სამუდამოდ წაშლის."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ეს სამუდამოდ წაშლის თქვენს ყველა მორგებულ მალსახმობს."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"მოქმედების ან მეტა კლავიშის ხატულა"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"პლუსის ხატულა"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"მორგება"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"გადაყენება"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"მზადაა"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"მალსახმობის დაყენება"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ამოშლა"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"დიახ, გადაყენდეს"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"გაუქმება"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"დააჭირეთ კლავიშს"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"კლავიშების კომბინაცია უკვე გამოიყენება. ცადეთ სხვა კლავიში."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ყოჩაღ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"თქვენ დაასრულეთ ყველა აპის ნახვის ჟესტი"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"სახელმძღვანელო ანიმაცია, დააწკაპუნეთ დასაპაუზებლად და გასაგრძელებლად."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 4f112933a76f..9b6dc953de12 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" таңбашасын қосу үшін параметрлерде \"Виджеттерді құлыптаулы экранда көрсету\" опциясының қосулы екенін тексеріңіз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Хабарландырулар"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Әңгімелер"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Барлық үнсіз хабарландыруларды өшіру"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Хабарландыру параметрлерін ашу"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Хабарландырулар Мазаламау режимінде кідіртілді"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Хабарландырулар жоқ.}=1{Хабарландыруларды {mode} режимі кідіртті.}=2{Хабарландыруларды {mode} және тағы бір режим кідіртті.}other{Хабарландыруларды {mode} және тағы # режим кідіртті.}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"қоңырау режимі"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, қоңырау режимін өзгерту үшін түртіңіз."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды құлыптау"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ескертпе жазу"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Мультитаскинг"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Қолданбаны бөлінген экранның оң жағынан пайдалану"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Қолданбаны бөлінген экранның сол жағынан пайдалану"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Бөлінген экран режимінен толық экран режиміне ауысу"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Дисплейлер арасында қосулы терезені ауыстыру"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Терезені сол жаққа жылжыту"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Терезені оң жаққа жылжыту"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Терезені ұлғайту"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Терезені кішірейту"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Енгізу"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Келесі тілге ауысу"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Алдыңғы тілге ауысу"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Жүйені басқару элементтері"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Жүйелік қолданбалар"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Соңғы қолданбалар"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлу"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Енгізу"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Қолданба таңбашалары"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Пернелер тіркесімін бейімдеу"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Жылдам пәрменді өшіру керек пе?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Әдепкі таңбашаларға қайтару керек пе?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Жылдам пәрменді тағайындау үшін пернені басыңыз."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Арнаулы жылдам пәрменіңіз біржола жойылады."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Мұндайда барлық арнаулы таңбашалар біржола жойылады."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Әрекет немесе Meta пернесінің белгішесі"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Қосу белгішесі"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Бейімдеу"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Бастапқы күйге қайтару"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Дайын"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"қосу"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"қиғаш сызық"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Жылдам пәрменді орнату"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Өшіру"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Иә, қайтару"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Бас тарту"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Пернені басыңыз"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Бұл пернелер тіркесімі қазір қолданыста. Басқа перне таңдаңыз."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Жарайсыз!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Барлық қолданбаны көру қимылын орындадыңыз."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Оқулықтың анимациясы, ойнатуды кідірту және жалғастыру үшін басыңыз."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 33aeb7028cd0..50f5fa05aff5 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុ​ក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុ​ក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ធាតុ​ក្រាហ្វិក"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ដើម្បីបញ្ចូលធាតុក្រាហ្វិកនៅលើអេក្រង់ចាក់សោជាផ្លូវកាត់ សូមប្រាកដថាវាត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ដើម្បីបញ្ចូលផ្លូវកាត់ \"ធាតុ​ក្រាហ្វិក\" ត្រូវប្រាកដថា \"បង្ហាញធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ\" ត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ការកំណត់"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"បង្ហាញប៊ូតុងធាតុ​រក្សា​អេក្រង់"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយ​ទាញចុះ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ការជូនដំណឹង"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ការសន្ទនា"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"សម្អាត​ការជូនដំណឹង​ស្ងាត់ទាំងអស់"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"បើកការកំណត់ការជូនដំណឹង"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ការជូនដំណឹង​បានផ្អាក​ដោយ​មុខងារកុំរំខាន"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{គ្មាន​ការជូន​ដំណឹង}=1{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode}}=2{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode} និង​មុខងារមួយ​ផ្សេងទៀត}other{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode} និង​មុខងារ # ផ្សេងទៀត}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើម​ឥឡូវ"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"រេតាមក្បាល"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ចុច​ដើម្បីប្ដូរ​មុខងារ​រោទ៍"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"មុខងារកម្មវិធី​រោទ៍"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> ចុចដើម្បីប្ដូរមុខងារកម្មវិធី​រោទ៍"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទ​សំឡេង"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើក​សំឡេង"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ចាក់​សោ​អេក្រង់"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"កត់​ចំណាំ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ការដំណើរការបានច្រើន"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីបច្ចុប្បន្ននៅខាងស្ដាំ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីបច្ចុប្បន្ននៅខាងឆ្វេង"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ប្ដូរពីមុខងារ​បំបែកអេក្រង់ទៅជាអេក្រង់ពេញ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ប្ដូរទៅកម្មវិធីនៅខាងស្ដាំ ឬខាងក្រោម ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ក្នុងអំឡុងពេលប្រើមុខងារបំបែកអេក្រង់៖ ជំនួសកម្មវិធីពីមួយទៅមួយទៀត"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ផ្លាស់ទីវិនដូដែលសកម្មរវាងផ្ទាំងអេក្រង់"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ផ្លាស់ទីវិនដូទៅឆ្វេង"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ផ្លាស់ទីវិនដូទៅស្ដាំ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ពង្រីកវិនដូ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"បង្រួមវិនដូ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"បញ្ចូល"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ប្ដូរទៅភាសាបន្ទាប់"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ប្ដូរទៅភាសាមុន"</string>
@@ -1419,34 +1422,35 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ការគ្រប់គ្រង​ប្រព័ន្ធ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធី​ប្រព័ន្ធ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"កម្មវិធី​ថ្មីៗ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"មុខងារ​បំបែកអេក្រង់"</string>
- <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ធាតុចូល"</string>
+ <string name="shortcut_helper_category_input" msgid="8674018654124839566">"វិធីបញ្ចូល"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ផ្លូវកាត់​កម្មវិធី"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"កម្មវិធីបច្ចុប្បន្ន"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់​ក្ដារ​ចុច"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ប្ដូរ​ផ្លូវកាត់​ក្ដារ​ចុចតាម​បំណង"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ដក​ផ្លូវកាត់​ចេញឬ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"កំណត់ឡើងវិញទៅលំនាំដើមឬ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ចុចគ្រាប់ចុច ដើម្បីកំណត់ផ្លូវ​កាត់"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ការធ្វើបែបនេះនឹងលុបផ្លូវ​កាត់ផ្ទាល់ខ្លួនរបស់អ្នកជាអចិន្ត្រៃយ៍។"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"សកម្មភាពនេះនឹងលុបផ្លូវកាត់ផ្ទាល់ខ្លួនរបស់អ្នកទាំងអស់ជាអចិន្ត្រៃយ៍។"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ស្វែងរកផ្លូវ​កាត់"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មាន​លទ្ធផល​ស្វែងរក​ទេ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"រូបគ្រាប់ចុចសកម្មភាព ឬមេតា"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"រូបសញ្ញាបូក"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ប្ដូរ​តាម​បំណង"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"កំណត់​ឡើងវិញ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"រួចរាល់"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"បូក"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"សញ្ញា (/)"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដង​អូស"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់​ក្ដារចុច"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"កំណត់ផ្លូវ​កាត់"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ដកចេញ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"បាទ/ចាស កំណត់ឡើងវិញ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"បោះបង់"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ចុចគ្រាប់ចុច"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"កំពុងប្រើបន្សំគ្រាប់ចុចស្រាប់ហើយ។ សាកល្បងប្រើគ្រាប់ចុចផ្សេង។"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ធ្វើបាន​ល្អ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"អ្នកបានបញ្ចប់ចលនាមើលកម្មវិធីទាំងអស់ហើយ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"រូបមានចលនាក្នុងអំឡុងមេរៀន ចុចដើម្បីផ្អាក រួចបន្តការចាក់ឡើងវិញ។"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រង​ផ្ទះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ee06f2340f73..a3718d3ffa96 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್‌ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ವಿಜೆಟ್‌ಗಳು"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ಶಾರ್ಟ್‌ಕಟ್‌ನಂತೆ ಲಾಕ್‌ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು, ಅದನ್ನು ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆಯೇ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ವಿಜೆಟ್‌ಗಳು\" ಶಾರ್ಟ್‌ಕಟ್ ಸೇರಿಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ \"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ತೋರಿಸಿ\" ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ಸ್ಕ್ರೀನ್‌ಸೇವರ್ ಬಟನ್ ತೋರಿಸಿ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್‌ಡೌನ್ ಮೆನು"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್‌ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ಸಂಭಾಷಣೆಗಳು"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ಎಲ್ಲಾ ನಿಶ್ಶಬ್ಧ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ನೋಟಿಫಿಕೇಶನ್‌ಗಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಎನ್ನುವ ಮೂಲಕ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ಯಾವುದೇ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು ಇಲ್ಲ}=1{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ವಿರಾಮಗೊಳಿಸಿದೆ}=2{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ ಒಂದು ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಿವೆ}one{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್‌ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}other{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್‌ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ಹೆಡ್ ಟ್ರ್ಯಾಕಿಂಗ್"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ರಿಂಗರ್ ಮೋಡ್ ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ರಿಂಗರ್ ಮೋಡ್"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ರಿಂಗರ್ ಮೋಡ್ ಅನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್‌"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಮಾಡಿ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ಟಿಪ್ಪಣಿಯನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ಬಲಭಾಗದಲ್ಲಿ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ಎಡಭಾಗದಲ್ಲಿ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್‌ನಿಂದ ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ಗೆ ಬದಲಿಸಿ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಬಲಭಾಗ ಅಥವಾ ಕೆಳಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಎಡಭಾಗ ಅಥವಾ ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸುವ ಸಮಯದಲ್ಲಿ: ಒಂದು ಆ್ಯಪ್‌ನಿಂದ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ಸಿಸ್ಟಂ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ಸಿಸ್ಟಂ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ಇನ್‌ಪುಟ್"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ಆ್ಯಪ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಬೇಕೇ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ಡೀಫಾಲ್ಟ್‌ಗೆ ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ನಿಯೋಜಿಸಲು ಕೀಯನ್ನು ಒತ್ತಿರಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ಇದು ನಿಮ್ಮ ಕಸ್ಟಮ್ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ಇದು ನಿಮ್ಮ ಎಲ್ಲಾ ಕಸ್ಟಮ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ಆ್ಯಕ್ಷನ್ ಅಥವಾ ಮೆಟಾ ಕೀ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ಪ್ಲಸ್ ಐಕಾನ್"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ಮುಗಿದಿದೆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ಶಾರ್ಟ್‌ಕಟ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ಹೌದು, ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ರದ್ದುಮಾಡಿ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ಕೀ ಅನ್ನು ಒತ್ತಿರಿ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ಕೀ ಸಂಯೋಜನೆಯು ಈಗಾಗಲೇ ಬಳಕೆಯಲ್ಲಿದೆ. ಮತ್ತೊಂದು ಕೀ ಬಳಸಿ ನೋಡಿ."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ಭೇಷ್!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ನೀವು ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳ ಜೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ಟ್ಯುಟೋರಿಯಲ್ ಅನಿಮೇಷನ್, ವಿರಾಮಗೊಳಿಸಲು ಮತ್ತು ಪ್ಲೇ ಪುನರಾರಂಭಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 6dcf92cfb0ba..ee19ad2c5af2 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"위젯"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"잠금 화면에 위젯을 바로가기로 추가하려면 설정에서 위젯을 사용 설정하세요."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\'위젯\' 바로가기를 추가하려면 설정에서 \'잠금 화면에 위젯 표시\'가 사용 설정되어 있어야 합니다."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"설정"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"알림"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"대화"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"무음 알림 모두 삭제"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"알림 설정 열기"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"방해 금지 모드로 알림이 일시중지됨"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{알림 없음}=1{{mode} 모드로 인해 알림이 일시중지되었습니다.}=2{{mode} 및 다른 모드로 인해 알림이 일시중지되었습니다.}other{{mode} 및 다른 모드 #개로 인해 알림이 일시중지되었습니다.}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"머리 추적"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"탭하여 벨소리 장치 모드 변경"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"벨소리 장치 모드"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, 탭하여 벨소리 장치 모드 변경"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"잠금 화면"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"메모 작성"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"멀티태스킹"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"현재 앱이 오른쪽에 오도록 화면 분할 사용"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"현재 앱이 왼쪽에 오도록 화면 분할 사용"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"화면 분할에서 전체 화면으로 전환"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"디스플레이 간 활성 창 이동"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"창을 왼쪽으로 이동"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"창을 오른쪽으로 이동"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"창 최대화"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"창 최소화"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"입력"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"다음 언어로 전환"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"이전 언어로 전환"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"시스템 컨트롤"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"시스템 앱"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"최근 앱"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"화면 분할"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 단축키"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"단축키 맞춤설정"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"바로가기를 제거하시겠습니까?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"기본값으로 재설정하시겠어요?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"키를 눌러 단축키 지정"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"맞춤 단축어가 영구적으로 삭제됩니다."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"모든 맞춤 바로가기가 완전히 삭제됩니다."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"작업 또는 메타 키 아이콘"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"더하기 아이콘"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"맞춤설정"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"재설정"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"완료"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"더하기"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"슬래시"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"단축키 설정"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"삭제"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"재설정"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"취소"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"키를 누르세요."</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"이미 사용 중인 키 조합입니다. 다른 키를 사용해 보세요."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"잘하셨습니다"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"모든 앱 보기 동작을 완료했습니다."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"튜토리얼 애니메이션입니다. 일시중지하고 재생을 재개하려면 클릭하세요."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5a58507e46a5..7ebfdec49c30 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"\"Кулпуланган экрандагы виджеттер\" функциясын ыкчам баскыч катары кошуу үчүн параметрлерге өтүп, анын иштетилгенин текшериңиз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" ыкчам баскычын кошуу үчүн параметрлерге өтүп, \"Виджеттерди кулпуланган экранда көрсөтүү\" параметри иштетилгенин текшериңиз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Билдирмелер"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Сүйлөшүүлөр"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Бардык үнсүз билдирмелерди өчүрүү"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Билдирмелердин параметрлерин ачуу"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"Тынчымды алба\" режиминде билдирмелер тындырылды"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Билдирмелер жок}=1{{mode} режими билдирмелерди тындырды}=2{{mode} жана дагы бир режим билдирмелерди тындырды}other{{mode} жана дагы # режим билдирмелерди тындырды}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Баштын кыймылына көз салуу"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"коңгуроо режими"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
@@ -868,9 +873,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды кулпулоо"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Кыска жазуу түзүү"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Бир нече тапшырма аткаруу"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Учурдагы колдонмону оңго жылдырып, экранды бөлүү"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Учурдагы колдонмону солго жылдырып, экранды бөлүү"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Экранды бөлүү режиминен толук экранга которулуу"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранда сол же төмөн жактагы колдонмого которулуу"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлүү режиминде бир колдонмону экинчисине алмаштыруу"</string>
@@ -1415,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системанын башкаруу элементтери"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системанын колдонмолору"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Акыркы колдонмолор"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлүү"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмонун ыкчам баскычтары"</string>
@@ -1424,14 +1431,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Ыкчам баскычтарды ыңгайлаштыруу"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ыкчам баскыч өчүрүлсүнбү?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Баштапкы абалга келтиресизби?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Ыкчам баскычты дайындоо үчүн баскычты басыңыз"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ушуну менен жеке ыкчам баскычыңыз биротоло өчүрүлөт."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ушуну менен жеке ыкчам баскычтарыңыздын баары биротоло өчүрүлөт."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Аракет же Мета ачкыч сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Кошуу сүрөтчөсү"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Ыңгайлаштыруу"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Баштапкы абалга келтирүү"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Бүттү"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
@@ -1441,6 +1451,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ыкчам баскычты тууралоо"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Өчүрүү"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ооба, баштапкы абалга келтирилсин"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Баскычты басыңыз"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ачкычтардын айкалышы колдонулууда. Башка ачкычты байкап көрүңүз."</string>
@@ -1472,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Эң жакшы!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Бардык колдонмолорду көрүү жаңсоосун аткардыңыз"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Үйрөткүч анимация, ойнотууну тындыруу же улантуу үчүн чыкылдатыңыз."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 73812c965a17..ae0006fbbf73 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -43,21 +43,21 @@
<item name="android:layout_marginTop">6dp</item>
<item name="android:textSize">36dp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Subtitle">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">6dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Description">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">6dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1cfd68ed3f81..151687fd4050 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ວິດເຈັດ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ເພື່ອເພີ່ມວິດເຈັດຢູ່ໜ້າຈໍລັອກເປັນທາງລັດ, ໃຫ້ແນ່ໃຈວ່າມັນຖືກເປີດການນຳໃຊ້ໃນການຕັ້ງຄ່າ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ເພື່ອເພີ່ມທາງລັດ \"ວິດເຈັດ\", ກະລຸນາກວດສອບວ່າໄດ້ເປີດການນຳໃຊ້ \"ສະແດງວິດເຈັດຢູ່ໜ້າຈໍລັອກ\" ໃນການຕັ້ງຄ່າແລ້ວ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ການຕັ້ງຄ່າ"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ປຸ່ມສະແດງພາບພັກໜ້າຈໍ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ການແຈ້ງເຕືອນ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ການສົນທະນາ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ລຶບລ້າງການແຈ້ງເຕືອນແບບງຽບທັງໝົດ"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ເປີດການຕັ້ງຄ່າການແຈ້ງເຕືອນ"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ຢຸດການແຈ້ງເຕືອນໂດຍໂໝດຫ້າມລົບກວນແລ້ວ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ບໍ່ມີການແຈ້ງເຕືອນ}=1{{mode} ຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}=2{{mode} ແລະ ອີກ 1 ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}other{{mode} ແລະ ອີກ # ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ການຕິດຕາມຫົວ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ໂໝດຣິງເກີ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ໜ້າຈໍລັອກ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ຈົດບັນທຶກ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ໃຊ້ການແບ່ງໜ້າຈໍກັບແອັບປັດຈຸບັນຢູ່ເບື້ອງຂວາ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ໃຊ້ການແບ່ງໜ້າຈໍກັບແອັບປັດຈຸບັນຢູ່ເບື້ອງຊ້າຍ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ສະຫຼັບຈາກແບ່ງໜ້າຈໍໄປເປັນເຕັມຈໍ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຂວາ ຫຼື ທາງລຸ່ມໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຊ້າຍ ຫຼື ທາງເທິງໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ໃນລະຫວ່າງແບ່ງໜ້າຈໍ: ໃຫ້ປ່ຽນຈາກແອັບໜຶ່ງເປັນອີກແອັບໜຶ່ງ"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ການຄວບຄຸມລະບົບ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ແອັບລະບົບ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ແອັບຫຼ້າສຸດ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ແບ່ງໜ້າຈໍ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ອິນພຸດ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ທາງລັດແອັບ"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ປັບແຕ່ງຄີລັດ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ລຶບທາງລັດອອກບໍ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ຣີເຊັດກັບຄືນເປັນຄ່າເລີ່ມຕົ້ນບໍ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ກົດປຸ່ມເພື່ອກຳນົດທາງລັດ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ການດຳເນີນການນີ້ຈະລຶບທາງລັດທີ່ກຳນົດເອງຂອງທ່ານຢ່າງຖາວອນ."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ການດຳເນີນການນີ້ຈະລຶບທາງລັດທີ່ກຳນົດເອງທັງໝົດຂອງທ່ານຢ່າງຖາວອນ."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ໄອຄອນຄຳສັ່ງ ຫຼື ປຸ່ມ Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ໄອຄອນໝາຍບວກ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ປັບແຕ່ງ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ຣີເຊັດ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ແລ້ວໆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ຕັ້ງທາງລັດ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ລຶບອອກ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ແມ່ນ, ຣີເຊັດ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ຍົກເລີກ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ກົດປຸ່ມ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ນໍາໃຊ້ປຸ່ມປະສົມຢູ່ແລ້ວ. ໃຫ້ລອງປຸ່ມອື່ນ."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ດີຫຼາຍ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບທັງໝົດສຳເລັດແລ້ວ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ພາບເຄື່ອນໄຫວຂອງບົດສອນການນຳໃຊ້, ຄລິກເພື່ອຢຸດຊົ່ວຄາວ ແລະ ສືບຕໍ່ຫຼິ້ນ."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 3c81e37d1233..91e17a71eaa3 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Valdikliai"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Jei norite pridėti valdiklių šaukinį užrakinimo ekrane, įsitikinkite, kad tai įgalinta nustatymuose."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jei norite pridėti valdiklių šaukinį, patikrinkite, ar nustatymuose įgalinta parinktis „Rodyti valdiklius užrakinimo ekrane“."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nustatymai"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Mygtukas „Rodyti ekrano užsklandą“"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pokalbiai"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Išvalyti visus tylius pranešimus"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Atidaryti pranešimų nustatymus"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pranešimai pristabdyti naudojant netrukdymo režimą"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nėra pranešimų}=1{Pranešimai pristabdyti naudojant režimą „{mode}“}=2{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar vieną režimą}one{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimą}few{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimus}many{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimo}other{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimų}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Galvos stebėjimas"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Palieskite, kad pakeistumėte skambučio režimą"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"skambučio režimas"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, palieskite, kad pakeistumėte skambučio režimą"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Užrakinti ekraną"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Sukurti pastabą"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kelių užduočių atlikimas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Naudoti išskaidyto ekrano režimą su dabartine programa dešinėje"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Naudoti išskaidyto ekrano režimą su dabartine programa kairėje"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Perjungti iš išskaidyto ekrano režimo į viso ekrano režimą"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Perjunkite į programą dešinėje arba apačioje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Perjunkite į programą kairėje arba viršuje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Išskaidyto ekrano režimu: pakeisti iš vienos programos į kitą"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemos valdikliai"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemos programos"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kelių užduočių atlikimas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Naujausios programos"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos spartieji klavišai"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sparčiųjų klavišų tinkinimas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Pašalinti spartųjį klavišą?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Iš naujo nustatyti numatytąjį nustatymą?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Paspauskite klavišą, kad priskirtumėte spartųjį klavišą"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bus visam laikui ištrintas tinkintas spartusis klavišas."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bus visam laikui ištrinti visi tinkinti šaukiniai."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ieškoti sparčiųjų klavišų"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Veiksmo arba metaduomenų klavišo piktograma"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pliuso piktograma"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tinkinti"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nustatyti iš naujo"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Atlikta"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nustatyti spartųjį klavišą"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Pašalinti"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Taip, nustatyti iš naujo"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atšaukti"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paspauskite klavišą"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klavišų derinys jau naudojamas. Bandykite naudoti kitą klavišą."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paspauskite klaviatūros veiksmų klavišą"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Puikiai padirbėta!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Atlikote visų programų peržiūros gestą"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Mokomoji animacija. Spustelėkite, kad pristabdytumėte ir tęstumėte atkūrimą."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5b553d9a58b6..c06c2bad6bbc 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Logrīki"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Lai varētu pievienot funkciju “Logrīki bloķēšanas ekrānā” kā saīsni, iestatījumos noteikti iespējojiet šo funkciju."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Lai pievienotu saīsni “Logrīki”, iestatījumos noteikti iespējojiet opciju “Rādīt logrīkus bloķēšanas ekrānā”."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Iestatījumi"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Sarunas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Notīrīt visus klusos paziņojumus"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Atvērt paziņojumu iestatījumus"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Paziņojumi pārtraukti, izmantojot iestatījumu “Netraucēt”"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nav paziņojumu}=1{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” dēļ}=2{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl viena režīma dēļ}zero{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}one{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīma dēļ}other{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"zvanītāja režīms"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; pieskarieties, lai mainītu zvanītāja režīmu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloķēt ekrānu"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Izveidot piezīmi"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Vairākuzdevumu režīms"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni labajā pusē"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni kreisajā pusē"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Pārslēgties no ekrāna sadalīšanas režīma uz pilnekrāna režīmu"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pāriet uz lietotni pa labi/lejā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekrāna sadalīšanas režīmā: pārvietot lietotni no viena ekrāna uz otru"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Pārvietot aktīvo logu starp displejiem"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pārvietot logu pa kreisi"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pārvietot logu pa labi"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimizēt logu"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizēt logu"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ievade"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pārslēgt uz nākamo valodu"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pārslēgt uz iepriekšējo valodu"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistēmas vadīklas"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistēmas lietotnes"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Pēdējās izmantotās lietotnes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrāna sadalīšana"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Īsinājumtaustiņu pielāgošana"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vai noņemt saīsni?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vai atiestatīt noklusējuma vērtības?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Lai piešķirtu īsinājumtaustiņu, nospiediet taustiņu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Veicot šo darbību, jūsu pielāgotā saīsne tiks neatgriezeniski izdzēsta."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Veicot šo darbību, visas jūsu pielāgotās saīsnes tiks neatgriezeniski dzēstas."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt saīsnes"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Darbību jeb meta taustiņa ikona"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszīmes ikona"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pielāgot"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Atiestatīt"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gatavs"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"uz priekšu vērstā slīpsvītra"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Iestatīt īsinājumtaustiņu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Noņemt"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jā, atiestatīt"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atcelt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nospiediet taustiņu"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Taustiņu kombinācija jau tiek izmantota. Izmēģiniet citu taustiņu."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Lieliski!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jūs sekmīgi veicāt visu lietotņu skatīšanas žestu."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Mācību animācija. Noklikšķiniet, lai pārtrauktu un atsāktu atskaņošanu."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 32ee791b376c..a1e6017b0b84 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"За да ја додадете „Виџети на заклучен екран“ како кратенка, проверете дали е овозможена во „Поставки“."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да ја додадете кратенката „Виџети“, погрижете се да биде овозможен „Прикажување виџети на заклучен екран“ во „Поставки“."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Поставки"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијава ќе се избришат."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Избриши ги сите бесчујни известувања"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известувањата се паузирани од „Не вознемирувај“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Нема известувања}=1{Известувањата ги паузираше {mode}}=2{Известувањата ги паузираа {mode} и уште еден режим}one{Известувањата ги паузираа {mode} и уште # режим}other{Известувањата ги паузираа {mode} и уште # режими}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Следење на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, допрете за да го промените режимот на ѕвончето"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
@@ -868,24 +874,23 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заклучете го екранот"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Фатете белешка"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Мултитаскинг"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користете поделен екран со тековната апликација оддесно"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користете поделен екран со тековната апликација одлево"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Префрлете се од поделен екран на цел екран"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете се на апликацијата десно или долу при користењето поделен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Движете го активниот прозорец меѓу екраните"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Премести го прозорецот налево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Премести го прозорецот надесно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Максимизирај го прозорецот"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Минимизирај го прозорецот"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Внесување"</string>
- <string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете на следниот јазик"</string>
- <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете на претходниот јазик"</string>
+ <string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете се на следниот јазик"</string>
+ <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете се на претходниот јазик"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Пристапете до емоџијата"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Пристапете до гласовното пишување"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Апликации"</string>
@@ -1419,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системски контроли"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системски апликации"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Неодамнешни апликации"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Поделен екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Внесување"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Кратенки за апликации"</string>
@@ -1428,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се отстрани кратенката?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се ресетира на стандардните поставки?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притиснете го копчето за да доделите кратенка"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ова ќе ја избрише вашата приспособена кратенка трајно."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ова ќе ги избрише трајно сите ваши приспособени кратенки."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона за дејство или копче за дејство"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона плус"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Приспособете"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ресетирај"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плус"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"коса црта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Поставете кратенка"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Отстрани"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетирај"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете го копчето"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинацијата на копчиња веќе се користи. Обидете се со друго копче."</string>
@@ -1478,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимација за упатство, кликнете за ја паузирате и да ја продолжите репродукцијата."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ed6653170df6..4dc40f834449 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്‌ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്‌ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"വിജറ്റുകൾ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"കുറുക്കുവഴിയായി ലോക്ക് സ്ക്രീനിൽ വിജറ്റുകൾ ചേർക്കാൻ, ക്രമീകരണത്തിൽ അത് പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"വിജറ്റുകൾ\" കുറുക്കുവഴി ചേർക്കാൻ, ക്രമീകരണത്തിൽ \"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ കാണിക്കുക\" പ്രവർത്തനക്ഷമമാക്കിയെന്ന് ഉറപ്പാക്കുക."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ക്രമീകരണം"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"സ്‌ക്രീൻ സേവർ ബട്ടൺ കാണിക്കുക"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"അറിയിപ്പുകൾ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"സംഭാഷണങ്ങൾ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"എല്ലാ നിശബ്‌ദ അറിയിപ്പുകളും മായ്ക്കുക"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"അറിയിപ്പ് ക്രമീകരണം തുറക്കുക"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ശല്യപ്പെടുത്തരുത്\' വഴി അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തി"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{അറിയിപ്പുകൾ ഒന്നുമില്ല}=1{{mode}, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}=2{{mode} എന്നതും മറ്റൊരു മോഡും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}other{{mode} എന്നതും മറ്റ് # മോഡുകളും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ഹെഡ് ട്രാക്കിംഗ്"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"റിംഗർ മോഡ്"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ലോക്ക് സ്‌ക്രീൻ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ഒരു കുറിപ്പെടുക്കുക"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"മൾട്ടിടാസ്‌കിംഗ്"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"വലതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ഇടതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"സ്‌ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറുക"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറുക"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"സ്‌ക്രീൻ വിഭജന മോഡിൽ: ഒരു ആപ്പിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുക"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"സജീവ വിൻഡോകൾ ഡിസ്‌പ്ലേകൾക്ക് ഇടയിൽ നീക്കുക"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"വിൻഡോ ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"വിൻഡോ വലത്തേക്ക് നീക്കുക"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"വിൻഡോ വലുതാക്കുക"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"വിൻഡോ ചെറുതാക്കുക"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ഇൻപുട്ട്"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"അടുത്ത ഭാഷയിലേക്ക് മാറുക"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"മുമ്പത്തെ ഭാഷയിലേക്ക് മാറുക"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"സിസ്‌റ്റം നിയന്ത്രണങ്ങൾ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"സിസ്‌റ്റം ആപ്പുകൾ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്‌കിംഗ്"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"സ്‌ക്രീൻ വിഭജന മോഡ്"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ഇൻപുട്ട്"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ആപ്പ് കുറുക്കുവഴികൾ"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"കീബോർഡ് കുറുക്കുവഴികൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"കുറുക്കുവഴി നീക്കം ചെയ്യണോ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ഡിഫോൾട്ടിലേക്ക് തിരികെ റീസെറ്റ് ചെയ്യണോ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"കുറുക്കുവഴി അസൈൻ ചെയ്യാൻ കീ അമർത്തുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ഇത് നിങ്ങളുടെ ഇഷ്‌ടാനുസൃത കുറുക്കുവഴി ശാശ്വതമായി ഇല്ലാതാക്കും."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"നിങ്ങളുടെ എല്ലാ ഇഷ്‍ടാനുസൃത കുറുക്കുവഴികളും ശാശ്വതമായി ഇത് ഇല്ലാതാക്കും."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ആക്ഷൻ/മെറ്റാ കീ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"പ്ലസ് ഐക്കൺ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"റീസെറ്റ് ചെയ്യുക"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"പൂർത്തിയായി"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"പ്ലസ്"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ഫോർവേഡ് സ്ലാഷ്"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"കുറുക്കുവഴി സജ്ജീകരിക്കുക"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"നീക്കം ചെയ്യുക"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ഉവ്വ്, റീസെറ്റ് ചെയ്യുക"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"റദ്ദാക്കുക"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"കീ അമർത്തുക"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"കീ കോമ്പിനേഷൻ ഇതിനകം ഉപയോഗത്തിലുണ്ട്. മറ്റൊരു കീ പരീക്ഷിക്കുക."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"അഭിനന്ദനങ്ങൾ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\'എല്ലാ ആപ്പുകളും കാണുക\' ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ട്യൂട്ടോറിയൽ ആനിമേഷൻ, താൽക്കാലികമായി നിർത്താനും പ്ലേ പുനരാരംഭിക്കാനും ക്ലിക്ക് ചെയ്യുക."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index f2863d098675..13436343e0c4 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджет"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Түгжээтэй дэлгэц дээр товчлол байдлаар виджет нэмэхийн тулд тохиргоонд виджетийг идэвхжүүлсэн эсэхийг нягтална уу."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджет\"-ийн товчлол нэмэхийн тулд \"Түгжээтэй дэлгэц дээр виджет харуулах\"-ыг тохиргоонд идэвхжүүлсэн эсэхийг нягтална уу."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Тохиргоо"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Мэдэгдлүүд"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Харилцан яриа"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Бүх чимээгүй мэдэгдлийг арилгах"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Мэдэгдлийн тохиргоог нээнэ"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Бүү саад бол горимын түр зогсоосон мэдэгдэл"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Мэдэгдэл байхгүй}=1{Мэдэгдлийг {mode} түр зогсоосон}=2{Мэдэгдлийг {mode} болон өөр нэг горим түр зогсоосон}other{Мэдэгдлийг {mode} болон өөр # горим түр зогсоосон}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Толгой хянах"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"хонхны горим"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Түгжээтэй дэлгэц"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Тэмдэглэл хөтлөх"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Олон ажил зэрэг хийх"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Одоогийн аппыг баруун талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Одоогийн аппыг зүүн талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Дэлгэц хуваахаас бүтэн дэлгэц рүү сэлгэх"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун талд эсвэл доор байх апп руу сэлгэ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Идэвхтэй цонхыг дэлгэц хооронд зөөх"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Цонхыг зүүн тийш зөөх"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Цонхыг баруун тийш зөөх"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Цонхыг томруулах"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Цонхыг багасгах"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Оролт"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Дараагийн хэл рүү сэлгэх"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Өмнөх хэл рүү сэлгэх"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системийн тохиргоо"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системийн аппууд"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Саяхны аппууд"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Дэлгэцийг хуваах"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Оролт"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Аппын товчлол"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Товчлуурын шууд холбоосыг өөрчлөх"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Товчлолыг хасах уу?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Өгөгдмөл рүү буцааж шинэчлэх үү?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Товчлол оноохын тулд товч дарна уу"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Энэ нь таны захиалгат товчлолыг бүрмөсөн устгана."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Энэ нь таны бүх захиалгат товчлолыг бүрмөсөн устгана."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Үйлдлийн товч буюу өөрөөр Мета товчийн дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Нэмэх дүрс тэмдэг"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Өөрчлөх"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Шинэчлэх"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Болсон"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"болон"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"урагшаа ташуу зураас"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Товчлол тохируулах"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Хасах"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Тэгье, шинэчилье"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Цуцлах"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Товч дарна уу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Товчийн хослолыг аль хэдийн ашиглаж байна. Өөр товч туршиж үзнэ үү."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Зааврын анимаци, түр зогсоохын тулд товшиж, үргэлжлүүлэн тоглуулна уу."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 35ee42f838ef..3f902d4e4ebe 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -113,7 +113,7 @@
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"संपूर्ण स्क्रीन रेकॉर्ड करा: %s"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्टी रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तुम्ही अ‍ॅप रेकॉर्ड करता, तेव्हा त्या अ‍ॅपमध्ये दाखवलेली किंवा प्ले केलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रेकॉर्ड करा"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रेकॉर्ड करण्यासाठी अ‍ॅप निवडा"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अ‍ॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्‍क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"विजेट शॉर्टकट म्हणून लॉक स्‍क्रीनवर जोडण्यासाठी, सेटिंग्जमध्ये ती सुरू असल्याची खात्री करा."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोडण्यासाठी, सेटिंग्जमध्ये \"लॉक स्‍क्रीनवर विजेट दाखवा\" सुरू असल्याची खात्री करा."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग्ज"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचना"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"संभाषणे"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"सर्व सायलंट सूचना साफ करा"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"नोटिफिकेशन सेटिंग्ज उघडा"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"व्यत्यय आणून नकाद्वारे सूचना थांबवल्या"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोणतेही नोटिफिकेशन नाही}=1{{mode} द्वारे नोटिफिकेशन थांबवली आहेत}=2{{mode} द्वारे आणि आणखी एका मोडद्वारे नोटिफिकेशन थांबवली आहेत}other{{mode} द्वारे आणि आणखी # मोडद्वारे नोटिफिकेशन थांबवली आहेत}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रॅकिंग"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, रिंगर मोड बदलण्यासाठी टॅप करा"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -868,9 +873,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"नोंद घ्या"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टिटास्किंग"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"सद्य ॲप उजवीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"सद्य ॲप डावीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीनवरून फुल स्क्रीनवर स्विच करा"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन वापरताना उजवीकडील किंवा खालील अ‍ॅपवर स्विच करा"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन वापरताना डावीकडील किंवा वरील अ‍ॅपवर स्विच करा"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीनदरम्यान: एक अ‍ॅप दुसऱ्या अ‍ॅपने बदला"</string>
@@ -1415,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्‍टीमची नियंत्रणे"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टीम अ‍ॅप्स"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किंग"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"अलीकडील अ‍ॅप्स"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"अ‍ॅप शॉर्टकट"</string>
@@ -1424,14 +1431,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट कस्टमाइझ करा"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"शॉर्टकट काढून टाकायचा आहे का?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"डीफॉल्टवर पुन्हा रीसेट करायचे आहे का?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करण्यासाठी की प्रेस करा"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यामुळे तुमचा कस्टम शॉर्टकट कायमचा हटवला जाईल."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"हे तुमचे सर्व कस्टम शॉर्टकट कायमचे हटवेल."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"कृती किंवा मेटा की आयकन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"अधिक आयकन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइझ करा"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रीसेट करा"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूर्ण झाले"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
@@ -1441,6 +1451,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करा"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"काढून टाका"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"होय, रीसेट करा"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करा"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की प्रेस करा"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"की कॉम्बिनेशन आधीपासून वापरले जात आहे. दुसरी की वापरून पहा."</string>
@@ -1472,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अ‍ॅक्शन की प्रेस करा"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"खूप छान!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तुम्ही ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्यूटोरियल अ‍ॅनिमेशन थांबवण्यासाठी किंवा पुन्हा सुरू करण्यासाठी प्ले करा वर क्लिक करा."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index cfb95a2b7908..0900288bd13f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Untuk menambahkan Widget pada skrin kunci sebagai pintasan, pastikan skrin kunci itu didayakan dalam tetapan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tunjukkan widget pada skrin kunci\" didayakan dalam tetapan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Tetapan"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Tunjukkan butang penyelamat skrin"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Perbualan"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kosongkan semua pemberitahuan senyap"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buka tetapan pemberitahuan"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pemberitahuan dijeda oleh Jangan Ganggu"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tiada pemberitahuan}=1{Pemberitahuan dijeda oleh {mode}}=2{Pemberitahuan dijeda oleh {mode} dan satu lagi mod yang lain}other{Pemberitahuan dijeda oleh {mode} dan # mod yang lain}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Penjejakan Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mod pendering"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ketik untuk menukar mod pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci skrin"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Catat nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Berbilang tugas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan skrin pisah dengan apl semasa pada sebelah kanan"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan skrin pisah dengan apl semasa pada sebelah kiri"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih daripada skrin pisah kepada skrin penuh"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Tukar kepada apl di sebelah kanan/bawah semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Semasa skrin pisah: gantikan apl daripada satu apl kepada apl lain"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Alihkan tetingkap aktif antara paparan"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Alihkan tetingkap ke sebelah kiri"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Alihkan tetingkap ke sebelah kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimumkan tetingkap"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimumkan tetingkap"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih kepada bahasa seterusnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih kepada bahasa sebelumnya"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kawalan sistem"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apl sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apl terbaharu"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skrin pisah"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sesuaikan pintasan papan kekunci"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Alih keluar pintasan?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Tetapkan kembali kepada lalai?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan kekunci untuk menetapkan pintasan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan memadamkan pintasan tersuai anda secara kekal."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tindakan ini akan memadamkan semua pintasan tersuai anda secara kekal."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon kekunci tindakan atau Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikon tambah"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tetapkan semula"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"tambah"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"garis condong ke hadapan"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tetapan Papan Kekunci"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tetapkan pintasan"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Alih keluar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ya, tetapkan semula"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan kekunci"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Gabungan kekunci sudah digunakan. Cuba kekunci lain."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan kekunci tindakan pada papan kekunci anda"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Syabas!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah melengkapkan gerak isyarat lihat semua apl"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animasi tutorial, klik untuk menjeda dan menyambung semula main."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 8cce3718a571..186b60f7b266 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ဝိဂျက်များ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"လော့ခ်မျက်နှာပြင်တွင် ဖြတ်လမ်းလင့်ခ်အဖြစ် ‘ဝိဂျက်များ’ ထည့်ရန် ၎င်းကို ဆက်တင်များတွင်ဖွင့်ထားကြောင်း သေချာပါစေ။"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“ဝိဂျက်များ” ဖြတ်လမ်းလင့်ခ်ထည့်ရန်အတွက် ဆက်တင်များတွင် “လော့ခ်မျက်နှာပြင်ပေါ်၌ ဝိဂျက်များပြရန်” ကိုဖွင့်ထားကြောင်း သေချာပါစေ။"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ဆက်တင်များ"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"စခရင်နားချိန်ပုံ ပြရန်ခလုတ်"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"အကြောင်းကြားချက်များ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"စကားဝိုင်းများ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"အသံတိတ် အကြောင်းကြားချက်များအားလုံးကို ရှင်းလင်းရန်"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"အကြောင်းကြားချက် ဆက်တင်များ ဖွင့်ရန်"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"အကြောင်းကြားချက်များကို \'မနှောင့်ယှက်ရ\' က ခေတ္တရပ်ထားသည်"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{အကြောင်းကြားချက် မရှိပါ}=1{{mode} က ခဏရပ်ထားသော အကြောင်းကြားချက်များ}=2{{mode} နှင့် အခြားမုဒ်တစ်ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}other{{mode} နှင့် အခြားမုဒ် # ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"အသံမြည်မုဒ်"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>၊ ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"လော့ခ်မျက်နှာပြင်"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"မှတ်စုရေးရန်"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"လက်ရှိအက်ပ်ကို ညာ၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"လက်ရှိအက်ပ်ကို ဘယ်၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"မျက်နှာပြင် ခွဲ၍ပြသမှုမှ မျက်နှာပြင်အပြည့်သို့ ပြောင်းရန်"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"မျက်နှာပြင်ခွဲ၍ပြသခြင်း သုံးစဉ် ညာ (သို့) အောက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"မျက်နှာပြင် ခွဲ၍ပြသစဉ်- အက်ပ်တစ်ခုကို နောက်တစ်ခုနှင့် အစားထိုးရန်"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"လက်ရှိဝင်းဒိုးကို ပြကွက်များအကြား ရွှေ့ခြင်း"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ဝင်းဒိုးကို ဘယ်ဘက်ရွှေ့ရန်"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ဝင်းဒိုးကို ညာဘက်ရွှေ့ရန်"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ဝင်ဒိုးကို ချဲ့ရန်"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ဝင်းဒိုးကို ချုံ့ရန်"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"စာရိုက်ခြင်း"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"နောက်ဘာသာစကားသို့ ပြောင်းရန်"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ယခင်ဘာသာစကားသို့ ပြောင်းရန်"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"စနစ် ထိန်းချုပ်မှုများ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"စနစ် အက်ပ်များ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"မကြာသေးမီက အက်ပ်များ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ထည့်သွင်းမှု"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"အက်ပ်ဖြတ်လမ်းလင့်ခ်များ"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"လက်ကွက်ဖြတ်လမ်းများကို စိတ်ကြိုက်လုပ်ခြင်း"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ဖြတ်လမ်းလင့်ခ် ဖယ်ရှားမလား။"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"မူရင်းသို့ ပြန်လည်ပြင်ဆင်သတ်မှတ်မလား။"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ဖြတ်လမ်းလင့်ခ်သတ်မှတ်ရန် ကီးကို နှိပ်ပါ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"၎င်းသည် သင့်စိတ်ကြိုက် ဖြတ်လမ်းလင့်ခ်ကို အပြီးဖျက်ပါမည်။"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"၎င်းသည် သင့်စိတ်ကြိုက်ဖြတ်လမ်းလင့်ခ်အားလုံးကို အပြီးဖျက်မည်။"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ဖြတ်လမ်းများ ရှာရန်"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"လုပ်ဆောင်ချက် (သို့) Meta ကီးသင်္ကေတ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"အပေါင်းသင်္ကေတ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"စိတ်ကြိုက်လုပ်ရန်"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ပြင်ဆင်သတ်မှတ်ရန်"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ပြီးပြီ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"အပေါင်း"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ညာဘက်မျဉ်းစောင်း"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ဖြတ်လမ်း သတ်မှတ်ရန်"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ဖယ်ရှားရန်"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ပြန်လည်ပြင်ဆင်သတ်မှတ်မည်"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားကီးကို စမ်းကြည့်ပါ။"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"အလွန်ကောင်းပါသည်။"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"အက်ပ်အားလုံးကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ရှင်းလင်းပို့ချချက် လှုပ်ရှားသက်ဝင်ပုံ၊ ခဏရပ်ပြီး ဆက်ဖွင့်ရန် နှိပ်ပါ။"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index c863d19ca393..1ef544b5473e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Moduler"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"For å legge til moduler på låseskjermen som en snarvei, sørg for at de er slått på i innstillingene."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"For å legge til «Moduler»-snarveien, sørg for at «Vis moduler på låseskjermen» er slått på i innstillingene."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Innstillinger"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Fjern alle lydløse varsler"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Åpne varslingsinnstillingene"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Varsler er satt på pause av «Ikke forstyrr»"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen varsler}=1{Varsler er satt på pause av {mode}}=2{Varsler er satt på pause av {mode} og én modus til}other{Varsler er satt på pause av {mode} og # moduser til}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hodesporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modus for ringeprogrammet"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, trykk for å endre ringemodus"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Låseskjerm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ta et notat"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Bruk delt skjerm med den nåværende appen til høyre"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Bruk delt skjerm med den nåværende appen til venstre"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bytt fra delt skjerm til fullskjerm"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bytt til appen til høyre eller under mens du bruker delt skjerm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"I delt skjerm: Bytt ut en app"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flytt det aktive vinduet mellom skjermer"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flytt vinduet til venstre"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flytt vinduet til høyre"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimer vinduet"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimerer vinduet"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Skrivespråk"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Bytt til neste språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Bytt til forrige språk"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemkontroller"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapper"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nylige apper"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delt skjerm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpass hurtigtastene"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vil du fjerne hurtigtasten?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du tilbakestille til standard?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Trykk på en tast for å tilordne hurtigtasten"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dette fører til at den egendefinerte hurtigtasten slettes permanent."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dette fører til at alle de egendefinerte snarveiene dine slettes permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Handlings- eller Meta-tast-ikon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpass"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tilbakestill"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Ferdig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"pluss"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"skråstrek"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Angi hurtigtast"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, tilbakestill"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinasjonen brukes allerede. Prøv en annen tast."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har fullført bevegelsen for å se alle apper"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Veiledningsanimasjon. Klikk for å sette avspillingen på pause og gjenoppta den."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 681e32884a44..9a97010037d1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेटहरू"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"विजेटहरू लक स्क्रिनमा सर्टकटका रूपमा हाल्न सेटिङमा गई यो सुविधा अन गरिएको छ भन्ने सुनिश्चित गर्नुहोस्।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" सर्टकट हाल्न सेटिङमा \"लक स्क्रिनमा विजेट देखाउनुहोस्\" नामक विकल्प अन गरिएको छ भन्ने सुनिश्चित गर्नुहोस्।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिङ"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रिनसेभर देखाउने बटन"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"वार्तालापहरू"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"सबै मौन सूचनाहरू हटाउनुहोस्"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"नोटिफिकेसन सेटिङ खोल्नुहोस्"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"बाधा नपुऱ्याउनुहोस् नामक मोडमार्फत पज पारिएका सूचनाहरू"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कुनै पनि नोटिफिकेसन छैन}=1{{mode} ले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}=2{{mode} र अन्य एक मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}other{{mode} र अन्य # वटा मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्र्याकिङ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिङ्गर मोड बदल्न ट्याप गर्नुहोस्"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"घण्टी बजाउने मोड"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, घण्टी बजाउने मोड बदल्न ट्याप गर्नुहोस्"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रिन लक गर्नुहोस्"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"नोट लेख्नुहोस्"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"हालको एप दायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"हालको एप बायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रिनको साटो फुल स्क्रिन प्रयोग गर्नुहोस्"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा दायाँ वा तलको एप चलाउनुहोस्"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रिन प्रयोग गरिएका बेला: एउटा स्क्रिनमा भएको एप अर्कोमा लैजानुहोस्"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"सक्रिय विन्डोलाई एउटा डिस्प्लेबाट सारेर अर्को डिस्प्लेमा लैजानुहोस्"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"विन्डो सारेर बायाँतिर लैजानुहोस्"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"विन्डो सारेर दायाँतिर लैजानुहोस्"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"विन्डो म्याक्सिमाइज गर्नुहोस्"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"विन्डो मिनिमाइज गर्नुहोस्"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"अर्को भाषा प्रयोग गर्नुहोस्"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"अघिल्लो भाषा प्रयोग गर्नुहोस्"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्टमसँग सम्बन्धित नियन्त्रणहरू"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम एपहरू"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"हालसालै चलाइएका एपहरू"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रिन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"एपका सर्टकटहरू"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"किबोर्डका सर्टकटहरू कस्टमाइज गर्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"सर्टकट हटाउने हो?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"सर्टकट रिसेट गरी डिफल्ट बनाउने हो?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"सर्टकट असाइन गर्न की थिच्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यसो गर्नुभयो भने तपाईंको कस्टम सर्टकट सदाका लागि मेटिने छ।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"तपाईंले यसो गर्नुभयो भने तपाईंका सबै कस्टम सर्टकटहरू सदाका लागि मेटाइने छन्।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"एक्सन वा Meta कीको आइकन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस आइकन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइज गर्नुहोस्"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रिसेट गर्नुहोस्"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूरा भयो"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"प्लस"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"फर्वार्ड स्ल्यास"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"सर्टकट सेट गर्नुहोस्"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाउनुहोस्"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"अँ, रिसेट गर्नुहोस्"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द गर्नुहोस्"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की थिच्नुहोस्"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"यो की कम्बिनेसन प्रयोग गरिसकिएको छ। अर्कै की प्रयोग गरी हेर्नुहोस्।"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले जेस्चर प्रयोग गरी सबै एपहरू हेर्ने तरिका सिक्नुभएको छ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्युटोरियलको एनिमेसन, पज वा सुचारु गर्न क्लिक गर्नुहोस्।"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c5c08b113ac1..e7fe53660aa2 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Als je Widgets als sneltoets wilt toevoegen aan het vergrendelscherm, zorg je dat deze is aangezet in de instellingen."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Als je de snelkoppeling Widgets wilt toevoegen, zorg je dat Widgets tonen op het vergrendelingsscherm aanstaat in de instellingen."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellingen"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Knop Screensaver tonen"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekken"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle stille meldingen wissen"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Instellingen voor meldingen openen"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Meldingen onderbroken door \'Niet storen\'"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen meldingen}=1{Meldingen onderbroken door {mode}}=2{Meldingen onderbroken door {mode} en 1 andere modus}other{Meldingen onderbroken door {mode} en # andere modi}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdtracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"beltoonmodus"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tik om de belsoftwaremodus te wijzigen"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Scherm vergrendelen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Notitie maken"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasken"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gesplitst scherm gebruiken met de huidige app aan de rechterkant"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gesplitst scherm gebruiken met de huidige app aan de linkerkant"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Van gesplitst scherm naar volledig scherm schakelen"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Naar de app rechts of onderaan gaan als je een gesplitst scherm gebruikt"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Naar de app links of bovenaan gaan als je een gesplitst scherm gebruikt"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijdens gesplitst scherm: een app vervangen door een andere"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Actief venster verplaatsen tussen schermen"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Venster naar links verplaatsen"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Venster naar rechts verplaatsen"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Venster maximaliseren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Venster minimaliseren"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Overschakelen naar volgende taal"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Overschakelen naar vorige taal"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systeemopties"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systeem-apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recente apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gesplitst scherm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-sneltoetsen"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sneltoetsen aanpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Sneltoets verwijderen?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetten naar standaard?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Druk op de toets om de sneltoets toe te wijzen"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hiermee wordt je aangepaste sneltoets definitief verwijderd."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hiermee worden al je aangepaste snelkoppelingen definitief verwijderd."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sneltoetsen zoeken"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icoon voor actie- of metatoets"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusicoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Aanpassen"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetten"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Toetsenbordinstellingen"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Sneltoets instellen"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Verwijderen"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, resetten"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuleren"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk op een toets"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Toetsencombinatie is al in gebruik. Probeer een andere toets."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Goed gedaan!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Je weet nu hoe je het gebaar Alle apps bekijken maakt"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial-animatie, klik om het afspelen te onderbreken en te hervatten."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b7d840ac9075..35704ea2fcaa 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ୱିଜେଟ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ଏକ ସର୍ଟକଟ ଭାବେ ଲକ ସ୍କ୍ରିନରେ ୱିଜାଟ ଯୋଗ କରିବାକୁ, ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ ଏହା ସେଟିଂସରେ ସକ୍ଷମ ହୋଇଛି।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ୱିଜେଟ\" ସର୍ଟକଟ ଯୋଗ କରିବାକୁ ସେଟିଂସରେ \"ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ\"କୁ ସକ୍ଷମ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ସେଟିଂସ"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ସ୍କ୍ରିନସେଭର ବଟନ ଦେଖାନ୍ତୁ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ସମସ୍ତ ନୀରବ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଖାଲି କରନ୍ତୁ"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ବିଜ୍ଞପ୍ତି ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ବିକଳ୍ପ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତି ପଜ୍‍ ହୋଇଛି"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ}=1{{mode} ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}=2{{mode} ଏବଂ ଅନ୍ୟ ଏକ ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}other{{mode} ଏବଂ ଅନ୍ୟ # ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ହେଡ ଟ୍ରାକିଂ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ରିଙ୍ଗର୍ ମୋଡ୍ ବଦଳାଇବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ରିଂଗର ମୋଡ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ରିଙ୍ଗର ମୋଡ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍‍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ଲକ ସ୍କ୍ରିନ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ଏକ ନୋଟ ଲେଖନ୍ତୁ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ଡାହାଣରେ ବର୍ତ୍ତମାନର ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ବାମରେ ବର୍ତ୍ତମାନର ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସୁଇଚ କରନ୍ତୁ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ସକ୍ରିୟ ୱିଣ୍ଡୋକୁ ଡିସପ୍ଲେଗୁଡ଼ିକ ମଧ୍ୟରେ ମୁଭ କରନ୍ତୁ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ୱିଣ୍ଡୋକୁ ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ୱିଣ୍ଡୋକୁ ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ୱିଣ୍ଡୋକୁ ବଡ଼ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ୱିଣ୍ଡୋକୁ ଛୋଟ କରନ୍ତୁ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ଇନପୁଟ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ପରବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ପୂର୍ବବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ସିଷ୍ଟମ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ସିଷ୍ଟମ ଆପ୍ସ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ବର୍ତ୍ତମାନର ଆପ୍ସ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ଇନପୁଟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ଆପ ସର୍ଟକଟ"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ସର୍ଟକଟକୁ କାଢ଼ି ଦେବେ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ଡିଫଲ୍ଟରେ ପୁଣି ରିସେଟ କରିବେ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ସର୍ଟକଟ ଆସାଇନ କରିବା ପାଇଁ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ଏହା ଆପଣଙ୍କ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ଏହା ଆପଣଙ୍କର ସମସ୍ତ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ଆକ୍ସନ କିମ୍ବା ମେଟା କୀ ଆଇକନ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ପ୍ଲସ ଆଇକନ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ରିସେଟ କରନ୍ତୁ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ହୋଇଗଲା"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ପ୍ଲସ"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ଫରୱାର୍ଡ ସ୍ଲାସ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ସର୍ଟକଟ ସେଟ କରନ୍ତୁ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ହଁ, ରିସେଟ କରନ୍ତୁ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"କୀ ଦବାନ୍ତୁ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"କୀ କମ୍ବିନେସନ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଉଛି। ଅନ୍ୟ ଏକ କୀ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ବହୁତ ବଢ଼ିଆ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ଆପଣ ସମସ୍ତ ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ଟ୍ୟୁଟୋରିଆଲ ଆନିମେସନ, ପ୍ଲେ କରିବା ବିରତ କରି ପୁଣି ଆରମ୍ଭ କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ।"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 1dd40793b53e..68cd2b42f069 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ਵਿਜੇਟ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਹ ਸੁਵਿਧਾ ਚਾਲੂ ਹੈ।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ਵਿਜੇਟ\" ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਸੈਟਿੰਗਾਂ ਵਿੱਚ \"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦਿਖਾਓ\" ਚਾਲੂ ਹੈ।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ਸੈਟਿੰਗਾਂ"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ਗੱਲਾਂਬਾਤਾਂ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ਸਾਰੀਆਂ ਸ਼ਾਂਤ ਸੂਚਨਾਵਾਂ ਕਲੀਅਰ ਕਰੋ"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ ਹੈ}=1{{mode} ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}=2{{mode} ਅਤੇ ਇੱਕ ਹੋਰ ਮੋਡ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}other{{mode} ਅਤੇ # ਹੋਰ ਮੋਡਾਂ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ਹੈੱਡ ਟਰੈਕਿੰਗ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ਰਿੰਗਰ ਮੋਡ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ਲਾਕ ਸਕ੍ਰੀਨ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ਨੋਟ ਲਿਖੋ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ਸੱਜੇ ਪਾਸੇ ਦਿੱਤੀ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ਖੱਬੇ ਪਾਸੇ ਦਿੱਤੀ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਤੋਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਸੱਜੇ ਜਾਂ ਹੇਠਾਂ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੌਰਾਨ: ਇੱਕ ਐਪ ਨਾਲ ਦੂਜੀ ਐਪ ਨੂੰ ਬਦਲੋ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ਕਿਰਿਆਸ਼ੀਲ ਵਿੰਡੋ ਨੂੰ ਇੱਕ ਤੋਂ ਦੂਜੇ ਡਿਸਪਲੇ \'ਤੇ ਲਿਜਾਓ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ਵਿੰਡੋ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਲਿਜਾਓ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ਵਿੰਡੋ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਲਿਜਾਓ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ਵਿੰਡੋ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ਵਿੰਡੋ ਨੂੰ ਛੋਟਾ ਕਰੋ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ਇਨਪੁੱਟ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ਅਗਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ਪਿਛਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ਸਿਸਟਮ ਸੰਬੰਧੀ ਕੰਟਰੋਲ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ਸਿਸਟਮ ਐਪਾਂ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ਹਾਲੀਆ ਐਪਾਂ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ਇਨਪੁੱਟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ਐਪ ਸ਼ਾਰਟਕੱਟ"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ਕੀ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ਕੀ ਵਾਪਸ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ \'ਤੇ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ਸ਼ਾਰਟਕੱਟ ਨਿਰਧਾਰਿਤ ਕਰਨ ਲਈ ਕੁੰਜੀ ਦਬਾਓ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵਿਉਂਤੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਸਾਰੇ ਵਿਉਂਤਬੱਧ ਸ਼ਾਰਟਕੱਟ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟ ਜਾਣਗੇ।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਸ਼ਾਰਟਕੱਟ ਖੋਜੋ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ਕਾਰਵਾਈ ਜਾਂ Meta ਕੁੰਜੀ ਪ੍ਰਤੀਕ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ਜੋੜ-ਚਿੰਨ੍ਹ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ਹੋ ਗਿਆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ਪਲੱਸ"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ਫਾਰਵਰਡ ਸਲੈਸ਼"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ਸ਼ਾਰਟਕੱਟ ਸੈੱਟ ਕਰੋ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ਹਟਾਓ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ਹਾਂ, ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ਰੱਦ ਕਰੋ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ਕੁੰਜੀ ਦਬਾਓ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ਕੁੰਜੀ ਸੁਮੇਲ ਪਹਿਲਾਂ ਹੀ ਵਰਤੋਂ ਵਿੱਚ ਹੈ। ਕੋਈ ਹੋਰ ਕੁੰਜੀ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ਬਹੁਤ ਵਧੀਆ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ਤੁਸੀਂ \'ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ਟਿਊਟੋਰੀਅਲ ਐਨੀਮੇਸ਼ਨ, ਰੋਕਣ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ।"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 82fe86153be2..a970e2891e61 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -116,7 +116,7 @@
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, nagrane zostanie wszystko, co jest na nim widoczne. Dlatego uważaj na hasła, dane do płatności, wiadomości, zdjęcia, nagrania audio czy filmy."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kiedy nagrywasz aplikację, wszystko, co jest w niej wyświetlane lub odtwarzane, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nagrywaj ekran"</string>
- <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybieranie aplikacji do nagrywania"</string>
+ <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybierz aplikację do nagrywania"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Nagrywaj dźwięk"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Dźwięki z urządzenia"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Dźwięki odtwarzane na urządzeniu, na przykład muzyka, połączenia i dzwonki"</string>
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widżety"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Aby dodać Widżety na ekranie blokady jako skrót, upewnij się, że ta opcja jest włączona w ustawieniach."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Aby dodać skrót „Widżety”, upewnij się, że opcja „Pokaż widżety na ekranie blokady” jest włączona w ustawieniach."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ustawienia"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Przycisk Pokaż wygaszacz ekranu"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Rozmowy"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Usuń wszystkie ciche powiadomienia"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otwórz ustawienia powiadomień"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Powiadomienia wstrzymane przez tryb Nie przeszkadzać"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Brak powiadomień}=1{Powiadomienia są wstrzymane przez tryb {mode}}=2{Powiadomienia są wstrzymane przez tryb {mode} i 1 inny tryb}few{Powiadomienia są wstrzymane przez tryb {mode} i # inne tryby}many{Powiadomienia są wstrzymane przez tryb {mode} i # innych trybów}other{Powiadomienia są wstrzymane przez tryb {mode} i # innego trybu}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Śledzenie głowy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"tryb dzwonka"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Kliknij, aby zmienić tryb dzwonka"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zablokuj ekran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Zanotuj"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Wielozadaniowość"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Podziel ekran z bieżącą aplikacją widoczną po prawej"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Podziel ekran z bieżącą aplikacją widoczną po lewej"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Przełącz podzielony ekran na pełny ekran"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Przełącz się na aplikację po prawej lub poniżej na podzielonym ekranie"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Przełącz się na aplikację po lewej lub powyżej na podzielonym ekranie"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Podczas podzielonego ekranu: zastępowanie aplikacji"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Przenieś aktywne okno na inny ekran"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Przenieś okno w lewo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Przenieś okno w prawo"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksymalizuj okno"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizuj okno"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Wprowadzanie"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Przełącz na następny język"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Przełącz na poprzedni język"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemowe elementy sterujące"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacje systemowe"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ostatnie aplikacje"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podzielony ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Dostosuj skróty klawiszowe"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Usunąć skrót?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Zresetować do ustawień domyślnych?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Naciśnij klawisz, aby przypisać skrót"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Spowoduje to trwałe usunięcie skrótu niestandardowego."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Spowoduje to trwałe usunięcie wszystkich skrótów niestandardowych."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klawisza działania/meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Dostosuj"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetuj"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotowe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ukośnik prawy"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ustawienia klawiatury"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ustaw skrót"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Usuń"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Tak, zresetuj"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anuluj"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Naciśnij klawisz"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacja klawiszy jest już używana. Użyj innego klawisza."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Brawo!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Znasz już gest wyświetlania wszystkich aplikacji"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacja z samouczkiem. Kliknij, aby wstrzymać lub wznowić odtwarzanie."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e65dccb022d6..f28de4ecc2fa 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Compatível com compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Aceita compartilhamento de áudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -334,7 +334,7 @@
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
- <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueado"</string>
+ <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueada"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar o recurso \"Widgets na tela de bloqueio\" como um atalho, verifique se os atalhos estão ativados nas configurações."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configurações de notificações"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover janela ativa entre telas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover janela para a esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover janela para a direita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar janela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar janela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Redefinir para o padrão?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Essa ação vai excluir permanentemente todos os seus atalhos personalizados."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de adição"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Redefinir"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"mais"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra para a direita"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, redefinir"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial. Clique para pausar ou retomar a reprodução."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3dcba3010e67..83aa9cf35d8f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar widgets ao ecrã de bloqueio como um atalho, certifique-se de que estão ativados nas definições."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho \"Widgets\", certifique-se de que a opção \"Mostrar widgets no ecrã de bloqueio\" está ativada nas definições."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Definições"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botão Mostrar proteção de ecrã"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Limpar todas as notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir definições de notificações"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações colocadas em pausa pelo modo Não incomodar."</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas pelo modo {mode} e mais um modo}many{Notificações pausadas pelo modo {mode} e mais # modos}other{Notificações pausadas pelo modo {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Posição da cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para alterar o modo de campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de som"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toque para alterar o modo de som"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecrã de bloqueio"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Tire notas"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Execução de várias tarefas em simultâneo"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use o ecrã dividido com a app atual à direita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use o ecrã dividido com a app atual à esquerda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar de ecrã dividido para ecrã inteiro"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para a app à direita ou abaixo enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para a app à esquerda ou acima enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante o ecrã dividido: substituir uma app por outra"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controlos do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Execução de várias tarefas em simultâneo"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecrã dividido"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalize os atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Repor para a predefinição?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Prima a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta ação elimina o atalho personalizado permanentemente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta ação elimina todos os seus atalhos personalizados permanentemente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla Meta ou de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de mais"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Repor"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Definições do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configurar atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, repor"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prima a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A combinação de teclas já está a ser usada. Experimente outra tecla."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prima a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Concluiu o gesto para ver todas as apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial, clique para pausar e retomar a reprodução."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e65dccb022d6..f28de4ecc2fa 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Compatível com compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Aceita compartilhamento de áudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -334,7 +334,7 @@
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
- <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueado"</string>
+ <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueada"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar o recurso \"Widgets na tela de bloqueio\" como um atalho, verifique se os atalhos estão ativados nas configurações."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configurações de notificações"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover janela ativa entre telas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover janela para a esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover janela para a direita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar janela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar janela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Redefinir para o padrão?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Essa ação vai excluir permanentemente todos os seus atalhos personalizados."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de adição"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Redefinir"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"mais"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra para a direita"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, redefinir"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial. Clique para pausar ou retomar a reprodução."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 93333c0db9ae..ea320a141c6e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeturi"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pentru a adăuga widgeturi pe ecranul de blocare drept comandă rapidă, verifică dacă sunt activate în setări."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pentru a adăuga comanda rapidă Widgeturi, verifică dacă opțiunea Afișează widgeturi pe ecranul de blocare este activată în setări."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setări"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Șterge toate notificările silențioase"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Deschide setările pentru notificări"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificări întrerupte prin „Nu deranja”"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nicio notificare}=1{Notificările au fost întrerupte de {mode}}=2{Notificările au fost întrerupte de {mode} și de un alt mod}few{Notificările au fost întrerupte de {mode} și de alte # moduri}other{Notificările au fost întrerupte de {mode} și de alte # de moduri}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Urmărirea mișcărilor capului"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modul sonerie"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, atinge pentru a schimba modul soneriei"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecranul de blocare"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Creează o notă"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Folosește ecranul împărțit cu aplicația curentă în dreapta"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Folosește ecranul împărțit cu aplicația curentă în stânga"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Comută de la ecranul împărțit la ecranul complet"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Treci la aplicația din dreapta sau de mai jos cu ecranul împărțit"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Treci la aplicația din stânga sau de mai sus cu ecranul împărțit"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"În modul ecran împărțit: înlocuiește o aplicație cu alta"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mută fereastra activă de pe un ecran pe altul"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mută fereastra spre stânga"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mută fereastra spre dreapta"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizează fereastra"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizează fereastra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Introducere"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Comută la următoarea limbă"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Comută la limba anterioară"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Comenzile sistemului"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicații de sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicații recente"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecran împărțit"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizează comenzile rapide de la tastatură"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Elimini comanda rapidă?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetezi la valorile prestabilite?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Apasă tasta pentru a atribui comanda rapidă"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Astfel, se va șterge definitiv comanda rapidă personalizată."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Astfel, se vor șterge definitiv toate comenzile rapide personalizate."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Pictograma pentru acțiune sau tastă Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pictograma plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizează"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetează"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gata"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"bară oblică spre dreapta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setează o comandă rapidă"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Elimină"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, resetează"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinația de taste este deja folosită. Încearcă altă tastă."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Felicitări!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ai finalizat gestul pentru afișarea tuturor aplicațiilor"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animat, dă clic pentru a întrerupe și a relua redarea."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 4641e016af66..f78593681f69 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеты"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Чтобы добавить виджеты на заблокированный экран, включите в настройках параметр \"Виджеты на заблокированном экране\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Чтобы создать ярлык \"Виджеты\", убедитесь, что в настройках включена функция \"Показывать виджеты на заблокированном экране\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Уведомления"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговоры"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Отклонить все беззвучные уведомления"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Открыть настройки уведомлений"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"В режиме \"Не беспокоить\" уведомления заблокированы"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Уведомлений нет}=1{Режим \"{mode}\" приостанавливает уведомления}=2{Режим \"{mode}\" и ещё один режим приостанавливают уведомления}one{Режим \"{mode}\" и ещё # режим приостанавливают уведомления}few{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}many{Режим \"{mode}\" и ещё # режимов приостанавливают уведомления}other{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Динамичное"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Нажмите, чтобы изменить режим звонка."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокировать экран"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Создать заметку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Многозадачность"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Разделить экран и поместить это приложение справа"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Разделить экран и поместить это приложение слева"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Изменить режим разделения экрана на полноэкранный режим"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти к приложению справа или внизу на разделенном экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"В режиме разделения экрана заменить одно приложение другим"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Переместить активное окно между экранами"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Переместить окно влево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Переместить окно вправо"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Развернуть окно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Свернуть окно"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ввод"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Выбрать следующий язык"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Выбрать предыдущий язык"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Управление системой"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системные приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Недавние приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделение экрана"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Быстрые клавиши для приложений"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Как настроить быстрые клавиши"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Удалить сочетание клавиш?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Сбросить настройки?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Нажмите клавишу, чтобы назначить сочетание клавиш."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Настроенное сочетание будет безвозвратно удалено."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Все пользовательские ярлыки будут безвозвратно удалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавиши Meta для выполнения действия"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Настроить"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Сбросить"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"косая черта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задать сочетание клавиш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Удалить"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Сбросить"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отмена"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Нажмите клавишу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Это сочетание клавиш уже используется. Попробуйте другое."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Блестяще!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы выполнили жест для просмотра всех приложений."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимация в руководстве. Нажмите, чтобы приостановить или продолжить воспроизведение."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 7f85b6f7387f..68cdef71535f 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"විජට්"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"කෙටිමඟක් ලෙස අගුළු තිරය මත Widgets එක් කිරීමට, එය සැකසීම් තුළ සබල කර ඇති බවට වග බලා ගන්න."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"විජට්\" කෙටිමඟ එක් කිරීමට, සැකසීම් තුළ \"අගුළු තිරයෙහි විජට් පෙන්වන්න\" සබල කර ඇති බවට වග බලා ගන්න."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"සැකසීම්"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"දැනුම් දීම්"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"සංවාද"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"සියලු නිහඬ දැනුම්දීම් හිස් කරන්න"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"දැනුම්දීම් සැකසීම් විවෘත කරන්න"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"බාධා නොකරන්න මගින් විරාම කරන ලද දැනුම්දීම්"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{දැනුම්දීම් නැත}=1{{mode} මගින් දැනුම්දීම් විරාම කරන ලදි}=2{{mode} සහ තව එක ප්‍රකාරයක් මගින් දැනුම්දීම් විරාම කරන ලදි}one{{mode} සහ තව ප්‍රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}other{{mode} සහ තව ප්‍රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"හිස ලුහුබැඳීම"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"හඬ නඟන ආකාරය"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, හඬ නඟන ප්‍රකාරය වෙනස් කිරීමට තට්ටු කරන්න"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"තිරය අගුළු දමන්න"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"සටහනක් ගන්න"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"බහුකාර්ය"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"දකුණේ වත්මන් යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"වම් පැත්තේ වත්මන් යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"බෙදුම් තිරයේ සිට පූර්ණ තිරයට මාරු වන්න"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"බෙදුම් තිරය භාවිත කරන අතරතුර දකුණේ හෝ පහළින් ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"බෙදුම් තිරය අතරතුර: යෙදුමක් එකකින් තවත් එකක් ප්‍රතිස්ථාපනය කරන්න"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"සක්‍රිය කවුළුව සංදර්ශක අතර ගෙන යන්න"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"කවුළුව වමට ගෙන යන්න"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"කවුළුව දකුණට ගෙන යන්න"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"කවුළුව විහිදන්න"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"කවුළුව කුඩා කරන්න"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ආදානය"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"මීළඟ භාෂාවට මාරු වන්න"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"පෙර භාෂාවට මාරු වන්න"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"පද්ධති පාලන"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"පද්ධති යෙදුම්"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"මෑත යෙදුම්"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"බෙදුම් තිරය"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ආදානය"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"යෙදුම් කෙටිමං"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"යතුරුපුවරු කෙටිමං අභිරුචිකරණය කරන්න"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"කෙටිමඟ ඉවත් කරන්න ද?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"පෙරනිමියට යළි සකසන්න ද?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"කෙටිමඟ පැවරීමට යතුර ඔබන්න"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"මෙය ඔබේ අභිරුචි කෙටිමඟ ස්ථිරවම මකනු ඇත."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"මෙය ඔබේ සියලු අභිරුචි කෙටිමං ස්ථිරවම මකනු ඇත."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්‍රතිඵල නැත"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ක්‍රියාව හෝ Meta යතුරු නිරූපකය"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ධන නිරූපකය"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"අභිරුචිකරණය කරන්න"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"යළි සකසන්න"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"නිමයි"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ධන"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ඉදිරියට ඉර"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"කෙටිමඟ සකසන්න"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ඉවත් කරන්න"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ඔව්, යළි සකසන්න"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"අවලංගු කරන්න"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"යතුර ඔබන්න"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"යතුරු සංයෝජනය දැනටමත් භාවිත වේ. වෙනත් යතුරක් උත්සාහ කරන්න."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"හොඳින් කළා!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ඔබ සියලු යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"නිබන්ධන සජීවීකරණය, ක්‍රීඩාව විරාම කිරීමට සහ නැවත ආරම්භ කිරීමට ක්ලික් කරන්න."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6dc7a66f7f8f..552b2e362eeb 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -116,7 +116,7 @@
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri nahrávaní celej obrazovky sa zaznamená všetko, čo sa na nej zobrazuje. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri nahrávaní aplikácie sa zaznamená všetko, čo sa v nej zobrazuje alebo prehráva. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrávať obrazovku"</string>
- <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Výber aplikácie na nahrávanie"</string>
+ <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vyberte aplikáciu, z ktorej chcete nahrávať"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávať zvuk"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zariadenia"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk zo zariadenia, napríklad hudba, hovory a tóny zvonenia"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikácie"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ak chcete na uzamknutú obrazovku pridať miniaplikácie ako odkaz, uistite sa, že sú v nastaveniach povolené."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ak chcete pridať odkaz Miniaplikácie, uistite sa, že v nastaveniach je zapnutá možnosť Zobrazovať miniaplikácie na uzamknutej obrazovke."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavenia"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzácie"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazať všetky tiché upozornenia"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvoriť nastavenia upozornení"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Upozornenia sú pozastavené režimom bez vyrušení"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žiadne upozornenia}=1{Upozornenia boli pozastavené režimom {mode}}=2{Upozornenia boli pozastavené režimom {mode} a jedným ďalším}few{Upozornenia boli pozastavené režimom {mode} a # ďalšími}many{Notifications paused by {mode} and # other modes}other{Upozornenia boli pozastavené režimom {mode} a # ďalšími}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledovanie hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvonenia"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, režim zvonenia zmeníte klepnutím"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknutie obrazovky"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Napísanie poznámky"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Rozdeliť obrazovku, aktuálna aplikácia vpravo"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Rozdeliť obrazovku, aktuálna aplikácia vľavo"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prepnutie rozdelenej obrazovky na celú"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prechod na aplikáciu vpravo alebo dole pri rozdelenej obrazovke"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Počas rozdelenej obrazovky: nahradenie aplikácie inou"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Presun aktívneho okna medzi obrazovkami"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Presun okna doľava"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Presun okna doprava"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximalizovanie okna"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizovanie okna"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prepnutie na ďalší jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prepnutie na predchádzajúci jazyk"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Ovládanie systému"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikácie"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedávne aplikácie"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skratky aplikácií"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prispôsobenie klávesových skratiek"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Chcete skratku odstrániť?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Chcete resetovať na predvolené nastavenie?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Stlačením klávesa priraďte skratku"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Týmto natrvalo odstránite vlastnú skratku."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Týmto natrvalo odstránite všetky vlastné odkazy."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prehľadávať skratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona akčného klávesa alebo metaklávesa"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prispôsobiť"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetovať"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"lomka"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavenia klávesnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastaviť skratku"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstrániť"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Áno, resetovať"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušiť"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stlačte kláves"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinácia klávesov sa už používa. Skúste iný kláves."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Dobre!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Použili ste gesto na zobrazenie všetkých aplikácií."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Výuková animácia, kliknutím pozastavíte alebo obnovíte prehrávanie."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 74fbd06e15b2..33d400ce80cc 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Pripomočki"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Če želite na zaklenjen zaslon dodati pripomočke kot bližnjico, morate to omogočiti v nastavitvah."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Če želite dodati bližnjico »Pripomočki«, v nastavitvah omogočite možnost »Prikaz pripomočkov na zaklenjenem zaslonu«."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavitve"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pokaži gumb za ohranjevalnik zaslona"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pogovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Brisanje vseh tihih obvestil"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Odpiranje nastavitev obvestil"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Prikazovanje obvestil je začasno zaustavljeno z načinom »ne moti«"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ni obvestil}=1{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode}}=2{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še enim drugim načinom}one{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugim načinom}two{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugima načinoma}few{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}other{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način zvonjenja"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dotaknite se, če želite spremeniti način zvonjenja."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -868,9 +872,12 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaklepanje zaslona"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ustvarjanje zapiska"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Večopravilnost"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Uporaba razdeljenega zaslona s trenutno aplikacijo na desni"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Uporaba razdeljenega zaslona s trenutno aplikacijo na levi"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Preklop iz razdeljenega zaslona v celozaslonski način"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Preklop na aplikacijo desno ali spodaj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Preklop na aplikacijo levo ali zgoraj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pri razdeljenem zaslonu: medsebojna zamenjava aplikacij"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemski kontrolniki"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Večopravilnost"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Razdeljen zaslon"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vnos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Bližnjice do aplikacij"</string>
@@ -1424,14 +1430,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagajanje bližnjičnih tipk"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite odstraniti bližnjico?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite ponastaviti na privzete bližnjice?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipko za dodelitev bližnjice"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"S tem boste trajno izbrisali bližnjico po meri."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"S tem boste trajno izbrisali vse bližnjice po meri."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Iskanje po bližnjicah"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke za dejanje ali metapodatke"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ponastavi"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Končano"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
@@ -1441,6 +1450,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavite bližnjico"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstrani"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, ponastavi"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Prekliči"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipko"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipk je že v uporabi. Poskusite z drugo tipko."</string>
@@ -1472,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipko za dejanja na tipkovnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvedli ste potezo za ogled vseh aplikacij"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija v vadnici, kliknite za začasno zaustavitev in nadaljevanje predvajanja."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index bd68061a13db..ce7c9c0f0af7 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -306,8 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ndarja e audios"</string>
- <!-- no translation found for quick_settings_bluetooth_device_audio_sharing_or_switch_active (8680997711431098238) -->
- <skip />
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Mbështet ndarjen e audios"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
@@ -530,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikacionet"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Për të shtuar \"Miniaplikacionet në ekranin e kyçjes\" si shkurtore, sigurohu që kjo veçori të jetë aktivizuar te cilësimet."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Për të shtuar shkurtoren e \"Miniaplikacioneve\", sigurohu që \"Shfaq miniaplikacionet në ekranin e kyçjes\" të jetë aktivizuar te cilësimet."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cilësimet"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -591,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Njoftimet"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Bisedat"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Pastro të gjitha njoftimet në heshtje"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Njoftimet janë vendosur në pauzë nga modaliteti \"Mos shqetëso\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Asnjë njoftim}=1{Njoftimet u vendosën në pauzë nga {mode}}=2{Njoftimet u vendosën në pauzë nga {mode} dhe një modalitet tjetër}other{Njoftimet u vendosën në pauzë nga {mode} dhe # modalitete të tjera}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Fillo tani"</string>
@@ -705,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ndjekja e lëvizjeve të kokës"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modaliteti i ziles"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Trokit për të ndryshuar modalitetin e ziles"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
@@ -869,21 +874,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekrani i kyçjes"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Mbaj një shënim"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kryerja e shumë detyrave"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Përdor ekranin e ndarë me aplikacionin aktual në të djathtë"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Përdor ekranin e ndarë me aplikacionin aktual në të majtë"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Kalo nga ekrani i ndarë në ekranin e plotë"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Kalo tek aplikacioni djathtas ose poshtë kur përdor ekranin e ndarë"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Kalo tek aplikacioni në të majtë ose sipër kur përdor ekranin e ndarë"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Gjatë ekranit të ndarë: zëvendëso një aplikacion me një tjetër"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Zhvendose dritaren aktive mes ekraneve"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Zhvendos dritaren në të majtë"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Zhvendos dritaren në të djathtë"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimizo dritaren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizo dritaren"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Hyrja"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Kalo te gjuha tjetër"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Kalo te gjuha e mëparshme"</string>
@@ -1420,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrollet e sistemit"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacionet e sistemit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplikacionet e fundit"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrani i ndarë"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
@@ -1429,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizo shkurtoret e tastierës"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Të hiqet shkurtorja?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Të rivendosen përsëri te parazgjedhjet?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Shtyp tastin për të caktuar shkurtoren"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Kjo do ta fshijë përgjithmonë shkurtoren tënde të personalizuar."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kjo do të fshijë përgjithmonë të gjitha shkurtoret e tua të personalizuara."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona e tastit të veprimit ose tastit Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona e plusit"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizo"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Rivendos"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"U krye"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"vizë e pjerrët djathtas"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Cakto shkurtoren"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hiq"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Po, rivendosi"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinimi i tasteve është tashmë në përdorim. Provo një tast tjetër."</string>
@@ -1479,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Shumë mirë!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Përfundove gjestin për shikimin e të gjitha aplikacioneve"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacioni udhëzues. Kliko për të vendosur në pauzë dhe për të vazhduar luajtjen."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index cc18b668a281..8ae54465d9a7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Да бисте додали виџете на закључани екран као пречицу, уверите се да је то омогућено у подешавањима."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Да бисте додали пречицу Виџети, уверите се да је у подешавањима омогућено Приказуј виџете на закључаном екрану."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Подешавања"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Дугме Прикажи чувар екрана"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Обавештења"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Конверзације"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Обришите сва нечујна обавештења"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Отворите подешавања обавештења"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Обавештења су паузирана режимом Не узнемиравај"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Нема обавештења}=1{Обавештења је паузирао {mode}}=2{Обавештења су паузирали {mode} и још један режим}one{Обавештења су паузирали {mode} и још # режим}few{Обавештења су паузирали {mode} и још # режима}other{Обавештења су паузирали {mode} и још # режима}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Праћење главе"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Додирните да бисте променили режим звона"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим звона"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, додирните да бисте променили режим звона"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Откључавање екрана"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Направи белешку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Обављање више задатака истовремено"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користи подељени екран са том апликацијом с десне стране"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користи подељени екран са том апликацијом с леве стране"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Пређи са подељеног екрана на цео екран"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређи у апликацију здесна или испод док је подељен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Премести активан прозор на следећи екран"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Померите прозор налево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Померите прозор надесно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Повећајте прозор"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Смањите прозор"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Унос"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Пређи на следећи језик"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пређи на претходни језик"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системске контроле"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системске апликације"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Обављање више задатака истовремено"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Недавне апликације"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Подељени екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Унос"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Пречице за апликације"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Прилагодите тастерске пречице"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Желите да уклоните пречицу?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Желите да ресетујете на подразумевано?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притисните тастер да бисте доделили пречицу"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Овим ћете трајно избрисати прилагођену пречицу."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Тиме ћете трајно избрисати све прилагођене пречице."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Претражите пречице"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона тастера за радњу или мета тастера"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона знака плус"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Прилагоди"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ресетуј"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плус"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"коса црта унапред"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Подеси пречицу"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Уклони"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетуј"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притисните тастер"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинација тастера се већ користи. Пробајте са другим тастером."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Одлично!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Довршили сте покрет за приказивање свих апликација."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимација водича, кликните да бисте паузирали и наставили репродукцију."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index fae3c11f1344..dcb1955bc87a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Om du vill lägga till widgetar på låsskärmen som en genväg måste du se till att de är aktiverade i inställningarna."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Om du vill lägga till genvägen Widgetar måste du se till att Visa widgetar på låsskärmen är aktiverat i inställningarna."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Inställningar"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Aviseringar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konversationer"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Rensa alla ljudlösa aviseringar"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Aviseringar har pausats via Stör ej"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Inga aviseringar}=1{Aviseringar har pausats av {mode}}=2{Aviseringar har pausats av {mode} och ett annat läge}other{Aviseringar har pausats av {mode} och # andra lägen}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Huvudspårning"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringsignalläge"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: Tryck för att ändra ringsignalens läge"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -868,21 +874,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skärmen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Anteckna"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multikörning"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Anänd delad skärm med den aktuella appen till höger"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Använd delad skärm med den aktuella appen till vänster"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Byt mellan delad skärm och helskärm"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till höger eller nedanför när du använder delad skärm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flytta det aktiva fönstret mellan skärmar"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flytta fönstret åt vänster"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flytta fönstret åt höger"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximera fönstret"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimera fönstret"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inmatning"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Byt till nästa språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Byt till föregående språk"</string>
@@ -1419,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systeminställningar"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemappar"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Senaste apparna"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inmatning"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
@@ -1428,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Anpassa kortkommandon"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vill du ta bort kortkommandot?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vill du återställa till standardinställningarna?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryck på tangenten för att tilldela ett kortkommando"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Det anpassade kortkommandot raderas permanent."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Alla anpassade genvägar raderas permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon för åtgärdstangent"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassa"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Återställ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"snedstreck"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ange kortkommando"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ta bort"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, återställ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryck på tangenten"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tangentkombinationen används redan. Testa en annan tangent."</string>
@@ -1478,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryck på åtgärdstangenten på tangentbordet"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra gjort!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du är klar med rörelsen för att se alla apparna."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation för guiden: Klicka för att pausa och återuppta uppspelningen."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d8581e8aa519..cd2c448716ff 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Wijeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ili kuweka kipengele cha Wijeti kwenye skrini iliyofungwa kiwe njia ya mkato, hakikisha kimewashwa katika mipangilio."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ili uweke njia ya mkato ya \"Wijeti\", hakikisha kuwa kitufe cha \"Onyesha wijeti kwenye skrini iliyofungwa\" kimewashwa katika mipangilio."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mipangilio"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mazungumzo"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Futa arifa zote zisizo na sauti"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Fungua mipangilio ya arifa"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kipengele cha Usinisumbue kimesitisha arifa"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Hakuna arifa}=1{Arifa zimesitishwa na {mode}}=2{Arifa zimesitishwa na {mode} na hali nyingine moja}other{Arifa zimesitishwa na {mode} na hali nyingine #}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Funga skrini"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Andika dokezo"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Majukumu mengi"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kulia"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kushoto"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Badilisha kutoka skrini iliyogawanywa utumie skrini nzima"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Badilisha ili uende kwenye programu iliyo kulia au chini unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ukigawanya skrini: badilisha kutoka programu moja hadi nyingine"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Hamisha dirisha linalotumika kati ya skrini moja na nyingine"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Sogeza dirisha kushoto"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Sogeza dirisha kulia"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Panua dirisha"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Punguza dirisha"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vifaa vya kuingiza data"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Badilisha utumie lugha inayofuata"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Badilisha utumie lugha iliyotangulia"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Vidhibiti vya mfumo"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Programu za mfumo"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Programu za hivi majuzi"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gawa skrini"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Weka mapendeleo ya mikato ya kibodi"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ungependa kuondoa njia ya mkato?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Ungependa kurejesha njia za mkato chaguomsingi?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Bonyeza kitufe ukabidhi njia ya mkato"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hatua hii itaondoa kabisa njia yako maalum ya mkato."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hatua hii itafuta kabisa njia zako zote maalum za mkato."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Aikoni ya kitufe cha Vitendo au cha Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Aikoni ya alama ya kujumlisha"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Weka mapendeleo"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Weka upya"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Nimemaliza"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"na"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"mkwaju wa mbele"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka njia ya mkato"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ondoa"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ndiyo, rejesha"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tayari unatumia mchanganyiko wa vitufe. Jaribu kitufe kingine."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vizuri sana!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Umekamilisha mafunzo ya mguso wa kuangalia programu zote"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Uhuishaji wa mafunzo, bofya ili usitishe na uendelee kucheza."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/styles.xml b/packages/SystemUI/res/values-sw600dp-land/styles.xml
index cde1a1373bed..45698f78d03a 100644
--- a/packages/SystemUI/res/values-sw600dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/styles.xml
@@ -22,20 +22,20 @@
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">36sp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Subtitle">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Description">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/styles.xml b/packages/SystemUI/res/values-sw600dp-port/styles.xml
index 85e7af6ee1de..8b3e6fde0b74 100644
--- a/packages/SystemUI/res/values-sw600dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/styles.xml
@@ -30,7 +30,7 @@
<item name="android:layout_marginTop">24dp</item>
<item name="android:textSize">36sp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/styles.xml b/packages/SystemUI/res/values-sw720dp-land/styles.xml
index e75173d152b0..451a9a901cce 100644
--- a/packages/SystemUI/res/values-sw720dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/styles.xml
@@ -22,21 +22,21 @@
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">36sp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Subtitle">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Description">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/styles.xml b/packages/SystemUI/res/values-sw720dp-port/styles.xml
index 85e7af6ee1de..8b3e6fde0b74 100644
--- a/packages/SystemUI/res/values-sw720dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/styles.xml
@@ -30,7 +30,7 @@
<item name="android:layout_marginTop">24dp</item>
<item name="android:textSize">36sp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index f2145ff5d1aa..ce630bbc1f1a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -117,7 +117,7 @@
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ஓர் ஆப்ஸை ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"திரையை ரெக்கார்டு செய்"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ரெக்கார்டு செய்ய ஆப்ஸைத் தேர்வுசெய்தல்"</string>
- <string name="screenrecord_audio_label" msgid="6183558856175159629">"ஆடியோவை ரெக்கார்டு செய்"</string>
+ <string name="screenrecord_audio_label" msgid="6183558856175159629">"ஆடியோவை ரெக்கார்டு செய்தல்"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"சாதன ஆடியோ"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"இசை, அழைப்புகள், ரிங்டோன்கள் போன்ற உங்கள் சாதனத்திலிருந்து வரும் ஒலி"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"மைக்ரோஃபோன்"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"விட்ஜெட்கள்"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"\'பூட்டுத் திரையில் விட்ஜெட்கள்\' அம்சத்தை ஷார்ட்கட்டாகச் சேர்க்க, அமைப்புகளில் அது இயக்கப்பட்டுள்ளதை உறுதிப்படுத்தவும்."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“விட்ஜெட்கள்” ஷார்ட்கட்டைச் சேர்க்க, அமைப்புகளில் “பூட்டுத் திரையில் விட்ஜெட்களைக் காட்டுதல்” அமைப்பு இயக்கப்பட்டிருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"அமைப்புகள்"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"அறிவிப்புகள்"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"உரையாடல்கள்"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"சைலன்ட் அறிவிப்புகள் அனைத்தையும் அழிக்கும்"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"அறிவிப்பு அமைப்புகளைத் திறக்கும்"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தின் மூலம் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{அறிவிப்புகள் இல்லை}=1{{mode} பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}=2{{mode} மற்றும் வேறொரு பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}other{{mode} மற்றும் வேறு # பயன்முறைகளால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ஹெட் டிராக்கிங்"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ரிங்கர் பயன்முறையை மாற்ற தட்டவும்"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ரிங்கர் பயன்முறை"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ரிங்கர் பயன்முறையை மாற்ற தட்டலாம்"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"பூட்டுத் திரை"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"குறிப்பெடுத்தல்"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"பல வேலைகளைச் செய்தல்"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"தற்போது உள்ள ஆப்ஸ் வலதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"தற்போது உள்ள ஆப்ஸ் இடதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"திரைப் பிரிப்பு பயன்முறையிலிருந்து முழுத்திரைக்கு மாற்றுதல்"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"திரைப் பிரிப்பின்போது: ஓர் ஆப்ஸுக்குப் பதிலாக மற்றொன்றை மாற்றுதல்"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"காட்சிகளுக்கு இடையே செயலில் உள்ள சாளரத்தை நகர்த்துதல்"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"சாளரத்தை இடதுபுறமாக நகர்த்துதல்"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"சாளரத்தை வலதுபுறமாக நகர்த்துதல்"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"சாளரத்தைப் பெரிதாக்குதல்"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"சாளரத்தைச் சிறிதாக்குதல்"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"உள்ளீடு"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"அடுத்த மொழிக்கு மாற்றுதல்"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"முந்தைய மொழிக்கு மாற்றுதல்"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"சிஸ்டம் கட்டுப்பாடுகள்"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"சிஸ்டம் ஆப்ஸ்"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"சமீபத்திய ஆப்ஸ்"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"திரைப் பிரிப்பு"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"உள்ளீடு"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ஆப்ஸ் ஷார்ட்கட்கள்"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"கீபோர்டு ஷார்ட்கட்களைப் பிரத்தியேகப்படுத்துதல்"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ஷார்ட்கட்டை அகற்றவா?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"மீண்டும் இயல்புநிலைக்கு மீட்டமைக்கவா?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ஷார்ட்கட்டை அமைக்க பட்டனை அழுத்துங்கள்"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்டை நிரந்தரமாக நீக்கும்."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்கள் அனைத்தையும் நிரந்தரமாக நீக்கும்."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ஷார்ட்கட்களைத் தேடுக"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ஆக்‌ஷன்/மெட்டா பட்டன் ஐகான்"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"பிளஸ் ஐகான்"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"பிரத்தியேகப்படுத்தும்"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"மீட்டமை"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"முடிந்தது"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"மற்றும்"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ஃபார்வர்டு ஸ்லாஷ்"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ஷார்ட்கட்டை அமையுங்கள்"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"அகற்று"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ஆம். மீட்டமை"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ரத்துசெய்"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"பட்டனை அழுத்துங்கள்"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"பட்டன் சேர்க்கை ஏற்கெனவே பயன்பாட்டில் உள்ளது. வேறொரு பட்டனைப் பயன்படுத்திப் பார்க்கவும்."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்‌ஷன் பட்டனை அழுத்தவும்"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"அருமை!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"அனைத்து ஆப்ஸையும் பார்ப்பதற்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"பயிற்சி அனிமேஷன், இடைநிறுத்தவும் மீண்டும் இயக்கவும் கிளிக் செய்யலாம்."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2c7279657ea1..20d4cf766d58 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్‌ను ఉపయోగించి యాప్‌ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్‌లు మీ లాక్ స్క్రీన్‌కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"విడ్జెట్‌లు"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"లాక్ స్క్రీన్‌లో విడ్జెట్‌లను షార్ట్‌కట్‌గా జోడించడానికి, ఇది సెట్టింగ్‌లలో ఎనేబుల్ చేసి ఉందని నిర్ధారించుకోండి."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"విడ్జెట్‌ల\" షార్ట్‌కట్‌ను జోడించడానికి, సెట్టింగ్‌లలో \"లాక్ స్క్రీన్‌లో విడ్జెట్‌లను చూపండి\" అనే ఆప్షన్‌ను ఎనేబుల్ చేసినట్లు నిర్ధారించుకోండి."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"సెట్టింగ్‌లు"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"స్క్రీన్ సేవర్ బటన్‌ను చూపండి"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్‌డౌన్ మెనూ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"నోటిఫికేషన్‌లు"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"సంభాషణలు"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"అన్ని నిశ్శబ్ద నోటిఫికేషన్‌లను క్లియర్ చేస్తుంది"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"నోటిఫికేషన్‌ల సెట్టింగ్‌లను తెరవండి"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"అంతరాయం కలిగించవద్దు ద్వారా నోటిఫికేషన్‌లు పాజ్ చేయబడ్డాయి"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{నోటిఫికేషన్‌లు ఏవీ లేవు}=1{{mode} ద్వారా నోటిఫికేషన్‌లు పాజ్ చేయబడ్డాయి}=2{నోటిఫికేషన్‌లు, {mode}, మరో ఒక మోడ్ ద్వారా పాజ్ చేయబడ్డాయి}other{నోటిఫికేషన్‌లు, {mode}, మరో # మోడ్‌ల ద్వారా పాజ్ చేయబడ్డాయి}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"హెడ్ ట్రాకింగ్"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"రింగర్ మోడ్"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్‌మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"లాక్ స్క్రీన్"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"నోట్‌ను రాయండి"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"మల్టీ-టాస్కింగ్"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"కుడివైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ఎడమవైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"స్ప్లిట్ స్క్రీన్‌ను ఫుల్ స్క్రీన్‌కు మార్చండి"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా కింద యాప్‌నకు మారండి"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్‌నకు మారండి"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"యాక్టివ్ విండోను డిస్‌ప్లేల మధ్య తరలించండి"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"విండోను ఎడమ వైపునకు తరలించండి"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"విండోను కుడి వైపునకు తరలించండి"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"విండోను విస్తరించండి"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"విండోను కుదించండి"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ఇన్‌పుట్"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"తర్వాత భాషకు స్విచ్ అవ్వండి"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"మునుపటి భాషకు స్విచ్ అవ్వండి"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"సిస్టమ్ కంట్రోల్స్"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"సిస్టమ్ యాప్‌లు"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ఇటీవలి యాప్‌లు"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"స్ప్లిట్ స్క్రీన్"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ఇన్‌పుట్"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"యాప్ షార్ట్‌కట్‌లు"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్‌కట్‌లు"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"కీబోర్డ్ షార్ట్‌కట్‌లను అనుకూలంగా మార్చండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"షార్ట్‌కట్‌ను తీసివేయాలా?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"తిరిగి ఆటోమేటిక్ సెట్టింగ్‌కు రీసెట్ చేయాలా?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"షార్ట్‌కట్‌ను కేటాయించడానికి కీని నొక్కండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ఇది మీ అనుకూల షార్ట్‌కట్‌ను శాశ్వతంగా తొలగిస్తుంది."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ఇది మీ అనుకూల షార్ట్‌కట్‌లన్నింటిని శాశ్వతంగా తొలగిస్తుంది."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"షార్ట్‌కట్‌లను వెతకండి"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"యాక్షన్ లేదా మెటా కీ చిహ్నం"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ప్లస్ చిహ్నం"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"అనుకూలంగా మార్చండి"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"రీసెట్ చేయండి"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"పూర్తయింది"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ప్లస్ గుర్తు"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ఫార్వర్డ్ స్లాష్"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్‌లు"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"షార్ట్‌కట్‌ను సెట్ చేయండి"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"తీసివేయండి"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"అవును, రీసెట్ చేయాలి"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"రద్దు చేయండి"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"కీని నొక్కండి"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"కీ కాంబినేషన్ ఇప్పటికే వినియోగంలో ఉంది. వేరొక కీని ట్రై చేయండి."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"చక్కగా చేశారు!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"అన్ని యాప్‌లను చూడడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ట్యుటోరియల్ యానిమేషన్, పాజ్ చేసి, మళ్లీ ప్లే చేయడానికి క్లిక్ చేయండి."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6ca696826be8..d438e4457778 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"วิดเจ็ต"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"หากต้องการเพิ่มวิดเจ็ตในหน้าจอล็อกเป็นทางลัด โปรดตรวจสอบว่าได้เปิดใช้วิดเจ็ตแล้วในการตั้งค่า"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"หากต้องการเพิ่มทางลัด \"วิดเจ็ต\" โปรดตรวจสอบว่าได้เปิดใช้ \"แสดงวิดเจ็ตในหน้าจอล็อก\" แล้วในการตั้งค่า"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"การตั้งค่า"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ปุ่มแสดงภาพพักหน้าจอ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"เปิดการตั้งค่าการแจ้งเตือน"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"หยุดการแจ้งเตือนชั่วคราวโดย \"ห้ามรบกวน\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ไม่มีการแจ้งเตือน}=1{หยุดการแจ้งเตือนชั่วคราวโดย {mode}}=2{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก 1 โหมด}other{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก # โหมด}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"การติดตามการเคลื่อนไหวของศีรษะ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"โหมดเสียงเรียกเข้า"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"ล็อกหน้าจอ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"จดโน้ต"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"การทํางานหลายอย่างพร้อมกัน"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"ใช้โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านขวา"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"ใช้โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านซ้าย"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"เปลี่ยนจากโหมดแยกหน้าจอเป็นเต็มหน้าจอ"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"เปลี่ยนไปใช้แอปทางด้านขวาหรือด้านล่างขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ระหว่างใช้โหมดแยกหน้าจอ: เปลี่ยนแอปหนึ่งเป็นอีกแอปหนึ่ง"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ย้ายหน้าต่างที่ใช้งานไปยังหน้าจอต่างๆ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ย้ายหน้าต่างไปทางซ้าย"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ย้ายหน้าต่างไปทางขวา"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ขยายหน้าต่างเต็มหน้าจอ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ย่อหน้าต่าง"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"อินพุต"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"เปลี่ยนเป็นภาษาถัดไป"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"เปลี่ยนเป็นภาษาก่อนหน้า"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"การควบคุมระบบ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"แอประบบ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"แอปล่าสุด"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"แยกหน้าจอ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"อินพุต"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"แป้นพิมพ์ลัดของแอป"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ปรับแต่งแป้นพิมพ์ลัด"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"นำแป้นพิมพ์ลัดออกใช่ไหม"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"รีเซ็ตกลับเป็นค่าเริ่มต้นไหม"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"กดแป้นเพื่อกำหนดแป้นพิมพ์ลัด"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองอย่างถาวร"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"การดำเนินการนี้จะลบทางลัดที่กำหนดเองทั้งหมดอย่างถาวร"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ไอคอนการดำเนินการหรือแป้น Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ไอคอนเครื่องหมายบวก"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ปรับแต่ง"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"รีเซ็ต"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"เสร็จสิ้น"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"บวก"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"เครื่องหมายทับ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ตั้งค่าแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"นำออก"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ใช่ รีเซ็ต"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ยกเลิก"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"กดแป้น"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"มีการใช้แป้นที่กดร่วมกันนี้แล้ว โปรดลองใช้แป้นอื่น"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดสำเร็จแล้ว"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ภาพเคลื่อนไหวของบทแนะนำ คลิกเพื่อหยุดชั่วคราวและเล่นต่อ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2e1d28684786..ee152600a3e5 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Mga Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para magdagdag ng Mga Widget sa lock screen bilang shortcut, tiyaking naka-enable ito sa mga setting."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para idagdag ang shortcut na \"Mga Widget,\" tiyaking naka-enable ang \"Ipakita ang mga widget sa lock screen\" sa mga setting."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mga Setting"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Button na ipakita ang screensaver"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mga Pag-uusap"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"I-clear ang lahat ng silent na notification"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buksan ang mga setting ng notification"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Mga notification na na-pause ng Huwag Istorbohin"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Walang notification}=1{Na-pause ng {mode} ang mga notification}=2{Na-pause ng {mode} at isa pang mode ang mga notification}one{Na-pause ng {mode} at # pang mode ang mga notification}other{Na-pause ng {mode} at # pang mode ang mga notification}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pag-track ng Ulo"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, i-tap para baguhin ang ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"I-lock ang screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Magtala"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Pag-multitask"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gumamit ng split screen nang nasa kanan ang kasalukuyang app"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gumamit ng split screen nang nasa kaliwa ang kasalukuyang app"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Lumipat sa full screen mula sa split screen"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Lumipat sa app sa kanan o ibaba habang ginagamit ang split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Habang nasa split screen: magpalit-palit ng app"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Ilipat ang aktibong window sa pagitan ng mga display"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Ilipat ang window sa kaliwa"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Ilipat ang window sa kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"I-maximize ang window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"I-minimize ang window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Lumipat sa susunod na wika"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Lumipat sa dating wika"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Mga kontrol ng system"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Mga system app"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Mga kamakailang app"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"I-customize ang mga keyboard shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Alisin ang shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"I-reset pabalik sa default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pindutin ang key para magtalaga ng shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Permanente nitong ide-delete ang iyong custom na shortcut."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Permanente nitong ide-delete ang lahat ng iyong custom na shortcut."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icon ng Action o Meta key"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icon na plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"I-customize"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"I-reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tapos na"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mga Setting ng Keyboard"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Magtakda ng shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Alisin"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oo, i-reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselahin"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pindutin ang key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ginagamit na ang kumbinasyon ng key. Sumubok ng ibang key."</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Magaling!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Nakumpleto mo ang galaw sa pag-view ng lahat ng app"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation ng tutorial, i-click para i-pause at ipagpatuloy ang paglalaro."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6628d23e6cab..c12f7b6ea7d7 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget\'lar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Kilit ekranında widget\'lar özelliğini kısayol olarak eklemek için ayarlarda bu özelliğin etkinleştirildiğinden emin olun."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widget\'lar\" kısayolunu eklemek için ayarlarda \"Widget\'ları kilit ekranında göster\" seçeneğinin etkinleştirildiğinden emin olun."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirimler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Görüşmeler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Sessiz bildirimlerin tümünü temizle"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bildirimler, Rahatsız Etmeyin özelliği tarafından duraklatıldı"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildirim yok}=1{Bildirimler {mode} tarafından duraklatıldı}=2{Bildirimler, {mode} ve bir diğer mod tarafından duraklatıldı}other{Bildirimler, {mode} ve # diğer mod tarafından duraklatıldı}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Şimdi başlat"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş Takibi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"telefon zili modu"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, telefon zili modunu değiştirmek için dokunun"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
@@ -868,21 +874,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilit ekranı"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Not al"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoklu görev"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sağdaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Soldaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana geç"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yap"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran etkinken: Bir uygulamayı başkasıyla değiştir"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Etkin pencereyi ekranlar arasında taşıma"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pencereyi sola taşı"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pencereyi sağa taşı"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Pencereyi ekranı kaplayacak şekilde büyüt"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pencereyi simge durumuna küçült"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Giriş"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Sonraki dile geç"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Önceki dile geç"</string>
@@ -1419,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistem kontrolleri"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem uygulamaları"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Son uygulamalar"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string>
@@ -1428,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klavye kısayollarını özelleştirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kısayol kaldırılsın mı?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Varsayılan kısayollara sıfırlansın mı?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Kısayol atamak için tuşa basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu işlem, özel kısayolunuzu kalıcı olarak siler."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu işlem, tüm özel kısayollarınızı kalıcı olarak siler."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"İşlem veya Meta tuşu simgesi"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Artı simgesi"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Özelleştir"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Sıfırla"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Bitti"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"artı"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"eğik çizgi"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Kısayol ayarla"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kaldır"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Evet, sıfırlansın"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş deneyin."</string>
@@ -1478,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Tebrikler!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tüm uygulamaları görüntüleme hareketini tamamladınız"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Eğitim animasyonu, oynatmayı duraklatmak ve sürdürmek için tıklayın."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 71845a3500fe..c2c3c61a49c7 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Щоб додати ярлик для опції \"Показувати віджети на заблокованому екрані\", переконайтеся, що її ввімкнено в налаштуваннях."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Щоб додати ярлик \"Віджети\", переконайтеся, що в налаштуваннях увімкнено опцію \"Показувати віджети на заблокованому екрані\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налаштування"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Сповіщення"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Розмови"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Очистити всі беззвучні сповіщення"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Режим \"Не турбувати\" призупинив сповіщення"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Немає сповіщень}=1{Режим \"{mode}\" призупинив надсилання сповіщень}=2{\"{mode}\" і ще один режим призупинили надсилання сповіщень}one{\"{mode}\" і ще # режим призупинили надсилання сповіщень}few{\"{mode}\" і ще # режими призупинили надсилання сповіщень}many{\"{mode}\" і ще # режимів призупинили надсилання сповіщень}other{\"{mode}\" і ще # режиму призупинили надсилання сповіщень}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Почати зараз"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Відстеження рухів голови"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Натисніть, щоб змінити режим дзвінка."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
@@ -868,21 +874,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокувати екран"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Створити нотатку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Багатозадачність"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Розділити екран і показувати поточний додаток праворуч"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Розділити екран і показувати поточний додаток ліворуч"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Перейти з розділення екрана на весь екран"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти до додатка праворуч або внизу на розділеному екрані"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Під час розділення екрана: замінити додаток іншим"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Перемістити активне вікно між дисплеями"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Перемістити вікно ліворуч"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Перемістити вікно праворуч"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Розгорнути вікно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Згорнути вікно"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Метод введення"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Вибрати наступну мову"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Вибрати попередню мову"</string>
@@ -1419,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Елементи керування системою"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системні додатки"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Багатозадачність"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Нещодавні додатки"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Розділити екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Введення"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Комбінації клавіш для додатків"</string>
@@ -1428,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Налаштуйте комбінації клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Видалити комбінацію клавіш?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Відновити комбінації клавіш за умовчанням?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натисніть клавішу, щоб призначити комбінацію клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Вашу власну комбінацію клавіш буде видалено назавжди."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усі ваші власні комбінації клавіш буде видалено назавжди."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавіші дії або метаклавіші"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок \"плюс\""</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Налаштувати"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Скинути"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"скісна риска"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Налаштувати комбінацію клавіш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Видалити"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, відновити"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасувати"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натисніть клавішу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбінація клавіш уже використовується. Спробуйте іншу клавішу."</string>
@@ -1478,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Чудово!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ви виконали жест для перегляду всіх додатків"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Навчальна анімація. Натисніть, щоб призупинити або відновити відтворення."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 514244f40304..8b3b9a026fe8 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ویجیٹس"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"مقفل اسکرین پر ویجیٹس کو شارٹ کٹ کے بطور شامل کرنے کے لیے یقینی بنائیں کہ یہ ترتیبات میں فعال ہے۔"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ویجیٹس\" شارٹ کٹ شامل کرنے کے لیے، یقینی بنائیں کہ \"مقفل اسکرین پر ویجیٹس دکھائیں\" ترتیبات میں فعال ہے۔"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ترتیبات"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"اسکرین سیور بٹن دکھائیں"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اطلاعات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"گفتگوئیں"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"سبھی خاموش اطلاعات کو صاف کریں"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"اطلاعات کی ترتیبات کھولیں"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ڈسٹرب نہ کریں\' کے ذریعے اطلاعات کو موقوف کیا گیا"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{کوئی اطلاع نہیں ہے}=1{{mode} کی طرف سے اطلاعات کو روک دیا گیا ہے}=2{{mode} اور ایک دوسرے موڈ کے ذریعہ اطلاعات کو روک دیا گیا ہے}other{{mode} اور # دیگر طریقوں کے ذریعے اطلاعات کو روک دیا گیا ہے}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"سر کی ٹریکنگ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"رنگر وضع تبدیل کرنے کیلئے تھپتھپائیں"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"رنگر موڈ"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"رنگر وضع تبدیل کرنے کیلئے <xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> تھپتھپائیں"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"اسکرین لاک کریں"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"نوٹ لیں"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ملٹی ٹاسکنگ"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"دائیں جانب موجودہ ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"بائیں جانب موجودہ ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"اسپلٹ اسکرین سے پوری سکرین پر سوئچ کریں"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"اسپلٹ اسکرین کا استعمال کرتے ہوئے دائیں یا نیچے ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"اسپلٹ اسکرین کے دوران: ایک ایپ کو دوسرے سے تبدیل کریں"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"فعال ونڈو کو ڈسپلیز کے مابین منتقل کریں"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ونڈو کو دائیں طرف منتقل کریں"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ونڈو کو بائیں طرف منتقل کریں"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ونڈو کو بڑا کریں"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ونڈو کو چھوٹا کریں"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ان پٹ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"اگلی زبان پر سوئچ کریں"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"پچھلی زبان پر سوئچ کریں"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"سسٹم کنٹرولز"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"سسٹم ایپس"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"حالیہ ایپس"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"اسپلٹ اسکرین"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ان پٹ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ایپ شارٹ کٹس"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"کی بورڈ شارٹ کٹس کو حسب ضرورت بنائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"شارٹ کٹ ہٹائیں؟"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ڈیفالٹ پر واپس ری سیٹ کریں؟"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"شارٹ کٹ تفویض کرنے کے لیے کلید کو دبائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"یہ آپ کا حسب ضرورت شارٹ کٹ مستقل طور پر حذف کر دے گا۔"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"یہ آپ کے تمام حسب ضرورت شارٹ کٹس کو مستقل طور پر حذف کر دے گا۔"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"‏کارروائی یا Meta کلید کا آئیکن"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"پلس کا آئیکن"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"حسب ضرورت بنائیں"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ری سیٹ کریں"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ہو گیا"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"پلس"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"فارورڈ سلیش"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"شارٹ کٹ سیٹ کریں"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ہٹائیں"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ہاں، ری سیٹ کریں"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"منسوخ کریں"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید کو دبائیں"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"کلیدی مجموعہ پہلے سے استعمال میں ہے۔ دوسری کلید آزمائیں۔"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس دیکھیں کا اشارہ مکمل کر لیا ہے"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ٹیوٹوریل اینیمیشن، روکنے کے لیے کلک کریں اور چلانا دوبارہ شروع کریں۔"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 777be070e971..dc01a492356d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidjetlar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ekran qulfiga yorliq sifatida vidjetlar kiritish uchun uning sozlamalarda yoqilganini tekshiring."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“Vidjetlar” yorligʻini qoʻshish uchun sozlamalarda “Vidjetlarni ekran qulfida chiqarish” yoqilganini tekshiring."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Sozlamalar"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Bildirishnoma sozlamalarini ochish"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilinadi"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildirishnomalar yoʻq}=1{{mode} rejimi bildirishnomalarni pauza qilgan}=2{{mode} va yana bitta boshqa rejim bildirishnomalarni pauza qilgan}other{{mode} va # ta boshqa rejim bildirishnomalarni pauza qilgan}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Boshni kuzatish"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"jiringlagich rejimi"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, jiringlagich rejimini oʻzgartirish uchun bosing"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekran qulfi"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Qayd yaratish"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-vazifalilik"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Ekranni ajratib, joriy ilovani oʻngga joylash"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ekranni ajratib, joriy ilovani chapga joylash"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Ajratilgan ekran rejimidan butun ekranga almashtirish"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ajratilgan ekranda oʻngdagi yoki pastdagi ilovaga almashish"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ajratilgan rejimda ilovalarni oʻzaro almashtirish"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Faol oynani ekranlararo koʻchirish"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Oynani chapga surish"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Oynani oʻngga surish"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Oynani yoyish"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Oynani kichraytirish"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Kiritish"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Keyingi tilga almashtirish"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Avvalgi tilga almashtirish"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Tizim boshqaruvi tugmalari"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Tizim ilovalari"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Oxirgi ilovalar"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekranni ikkiga ajratish"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tezkor tugmalarni moslash"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tezkor tugma olib tashlansinmi?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Asliga qaytarilsinmi?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tezkor tugma sozlash uchun tugmani bosing"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bunda maxsus tezkor tugma butunlay oʻchirib tashlanadi."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bunda barcha maxsus yorliqlaringiz butunlay oʻchirib tashlanadi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Amal bajarish uchun Meta tugmasi belgisi"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus belgisi"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Moslash"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tiklash"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tayyor"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"oldinga qiya chiziq"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura sozlamalari"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tezkor tugma sozlash"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Olib tashlash"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ha, asliga qaytarilsin"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Bekor qilish"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tugmani bosing"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Bu tugmalar birikmasi band. Boshqasini ishlating."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturadagi amal tugmasini bosing"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Barakalla!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hamma ilovalarni koʻrish ishorasini tugalladingiz"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Qoʻllanma animatsiyasi, pauza qilish va ijroni davom ettirish uchun bosing."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6ea683a5af09..1dd604273d00 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Tiện ích"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Để thêm Tiện ích dưới dạng lối tắt trên màn hình khoá, hãy đảm bảo bạn đã bật tính năng này trong phần cài đặt."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Để thêm phím tắt \"Tiện ích\", hãy nhớ bật tuỳ chọn \"Hiện tiện ích trên màn hình khoá\" trong phần cài đặt."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cài đặt"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Cuộc trò chuyện"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Xóa tất cả thông báo im lặng"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Mở phần cài đặt thông báo"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Chế độ Không làm phiền đã tạm dừng thông báo"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Không có thông báo}=1{{mode} đã tạm dừng thông báo}=2{{mode} và một chế độ khác đã tạm dừng thông báo}other{{mode} và # chế độ khác đã tạm dừng thông báo}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Theo dõi chuyển động của đầu"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"chế độ chuông"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, nhấn để thay đổi chế độ chuông"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Màn hình khoá"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Tạo ghi chú"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Đa nhiệm"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên phải"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên trái"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Chuyển từ chế độ chia đôi màn hình sang chế độ toàn màn hình"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Chuyển sang ứng dụng bên phải hoặc ở dưới khi đang chia đôi màn hình"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Chuyển sang ứng dụng bên trái hoặc ở trên khi đang chia đôi màn hình"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Trong chế độ chia đôi màn hình: thay một ứng dụng bằng ứng dụng khác"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Di chuyển cửa sổ đang hoạt động giữa các màn hình"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Di chuyển cửa sổ sang trái"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Di chuyển cửa sổ sang phải"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Phóng to cửa sổ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Thu nhỏ cửa sổ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Đầu vào"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Chuyển sang ngôn ngữ tiếp theo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Chuyển về ngôn ngữ trước"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Điều khiển hệ thống"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ứng dụng hệ thống"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ứng dụng gần đây"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Chia đôi màn hình"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lối tắt ứng dụng"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá lối tắt?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Đặt lại về phím tắt mặc định?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nhấn phím để chỉ định lối tắt"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn lối tắt tuỳ chỉnh của bạn."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Thao tác này sẽ xoá vĩnh viễn mọi phím tắt tuỳ chỉnh của bạn."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Biểu tượng phím Meta (phím hành động)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Biểu tượng dấu cộng"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tuỳ chỉnh"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Đặt lại"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Xong"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"dấu cộng"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"dấu gạch chéo lên"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Đặt phím tắt"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Xoá"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Có, đặt lại"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Huỷ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nhấn phím"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tổ hợp phím đã được sử dụng. Hãy thử một phím khác."</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Rất tốt!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Bạn đã hoàn tất cử chỉ xem tất cả các ứng dụng"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ảnh động trong phần hướng dẫn, nhấp để tạm dừng và tiếp tục phát."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index bc9c31849549..e18fcec9f897 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"微件"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要将微件作为快捷方式添加到锁屏界面,请确保已在设置中启用该功能。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要添加“微件”快捷方式,请确保已在设置中启用“在锁屏状态下显示微件”。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"设置"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"“显示屏保”按钮"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"对话"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有静音通知"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"打开通知设置"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"勿扰模式暂停的通知"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{无通知}=1{{mode}暂停了通知}=2{{mode}和另外 1 种模式暂停了通知}other{{mode}和另外 # 种模式暂停了通知}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部跟踪"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,点按即可更改响铃模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"锁定屏幕"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"添加记事"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多任务处理"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分屏模式,并将当前应用置于右侧"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分屏模式,并将当前应用置于左侧"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"从分屏模式切换为全屏"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"在分屏期间:将一个应用替换为另一个应用"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在各个显示屏之间移动活动窗口"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"将窗口移至左侧"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"将窗口移至右侧"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"最大化窗口"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"最小化窗口"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"输入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切换到下一种语言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切换到上一种语言"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系统控件"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系统应用"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近用过的应用"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分屏"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"输入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"应用快捷键"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自定义键盘快捷键"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快捷键吗?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重置为默认快捷方式吗?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按键即可指定快捷键"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"此操作会永久删除您的自定义快捷键。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"此操作会永久删除您的所有自定义快捷方式。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"操作键或元键图标"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加号图标"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自定义"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重置"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加号"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"正斜线"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"设置快捷键"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,重置"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按键"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"按键组合已被使用,请尝试使用其他按键。"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势教程"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教程动画,点击可暂停和继续播放。"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 21477431d8fc..871bd31bbbd3 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -449,8 +449,8 @@
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
- <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
- <string name="zen_mode_on_with_details" msgid="7416143430557895497">"開 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="zen_mode_on" msgid="9085304934016242591">"已開啟"</string>
+ <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已開啟 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string>
<string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string>
@@ -529,7 +529,9 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要將小工具新增為上鎖畫面上的捷徑,請確認已在設定中啟用此功能。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請確保在設定中已啟用「在上鎖畫面顯示小工具」。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+ <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"顯示螢幕保護程式按鈕"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -590,6 +592,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有靜音通知"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"打開通知設定"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「請勿騷擾」模式已將通知暫停"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{{mode}已暫停通知}=2{{mode}和另外一個模式已暫停通知}other{{mode}和另外 # 個模式已暫停通知}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"響鈴模式"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,輕按以變更響鈴模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -868,21 +872,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"上鎖畫面"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"寫筆記"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多工處理"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分割螢幕,並在右側顯示目前使用的應用程式"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分割螢幕,並在左側顯示目前使用的應用程式"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"將分割螢幕切換為全螢幕"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割螢幕時,切換至右邊或下方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割螢幕期間:更換應用程式"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在不同畫面間移動使用中的視窗"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"將視窗移到左邊"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"將視窗移到右邊"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"將視窗放到最大"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"將視窗縮到最小"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切換至下一個語言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換至上一個語言"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系統控制項"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使用的應用程式"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割螢幕"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
@@ -1428,25 +1430,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設至預設捷徑嗎?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按鍵即可指派快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這將永久刪除你的自訂快速鍵。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這將永久刪除你的所有自訂捷徑。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重設"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"正斜線"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按鍵"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"此按鍵組合已在使用,請改用其他按鍵。"</string>
@@ -1478,6 +1482,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"做得好!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢的教學課程"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教學動畫,按一下以暫停和繼續播放。"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 13fb81c64450..334117540509 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要將小工具新增為螢幕鎖定畫面上的捷徑,請確認已在設定中啟用這項功能。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請務必前往設定啟用「在螢幕鎖定畫面上顯示小工具」。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string>
@@ -590,6 +593,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有靜音通知"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"開啟通知設定"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「零打擾」模式已將通知設為暫停"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{「{mode}」模式已將通知設為暫停}=2{「{mode}」和另一個模式已將通知設為暫停}other{「{mode}」和另外 # 個模式已將通知設為暫停}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -704,6 +708,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕觸即可變更鈴聲模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"鈴聲模式"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,輕觸即可變更鈴聲模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -868,21 +873,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"螢幕鎖定"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"新增記事"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多工處理"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分割畫面,並在右側顯示目前使用的應用程式"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分割畫面,並在左側顯示目前使用的應用程式"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"從分割畫面切換到完整畫面"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割畫面時,切換到右邊或上方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割畫面期間:更換應用程式"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在不同畫面間移動使用中的視窗"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"將視窗移至左側"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"將視窗移至右側"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"將視窗放到最大"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"將視窗縮到最小"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切換到下一個語言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換到上一個語言"</string>
@@ -1419,7 +1423,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系統控制選項"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使用的應用程式"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割畫面"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
@@ -1428,25 +1431,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設為預設值嗎?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按鍵即可指派快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這項操作會永久刪除自訂快速鍵。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這麼做會永久刪除所有快速鍵。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重設"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"斜線"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"這個按鍵組合已在使用中,請改用其他按鍵。"</string>
@@ -1478,6 +1483,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常好!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢教學課程"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教學課程動畫,按一下即可暫停和繼續播放。"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index f3230218ca65..a9fa1ba36df2 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Amawijethi"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ukufaka Amawijethi esikrinini sokukhiya njengesinqamuleli, qinisekisa ukuthi inikwe amandla kumasethingi."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ukuze ufake isinqamuleli esithi \"Amawijethi\", qinisekisa ukuthi okuthi \"Bonisa amawijethi esikrinini sokukhiya\" kunikwe amandla kumasethingi."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Amasethingi"</string>
+ <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Izaziso"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Izingxoxo"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Sula zonke izaziso ezithulile"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Izaziso zimiswe okwesikhashana ukungaphazamisi"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Azikho izaziso}=1{Izaziso zimiswe okwesikhashana yi-{mode}}=2{Izaziso zimiswe okwesikhashana yi-{mode} nelinye imodi elilodwa}one{Izaziso zimiswe okwesikhashana yi-{mode} kanye namanye amamodi angu-#}other{Izaziso zimiswe okwesikhashana yi-{mode} kanye namanye amamodi angu-#}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Qala manje"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ukulandelela Ikhanda"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"imodi yokukhala"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, thepha ukuze ushintshe imodi yokukhala"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
@@ -868,21 +874,20 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Khiya isikrini"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Thatha inothi"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Ukwenza imisebenzi eminingi"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sebenzisa isikrini esihlukanisayo nge-app yamanje kwesokudla"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Sebenzisa isikrini sokuhlukanisa nge-app yamanje kwesokunxele"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Shintsha usuka ekuhlukaniseni isikrini uye kusikrini esigcwele"</string>
+ <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+ <skip />
+ <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+ <skip />
+ <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+ <skip />
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Shintshela ku-app ngakwesokudla noma ngezansi ngenkathi usebenzisa uhlukanisa isikrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ngesikhathi sokuhlukaniswa kwesikrini: shintsha i-app ngenye"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Hambisa iwindi elisebenzayo phakathi kwezibonisi"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Hambisa iwindi liye kwesokudla"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Hambisa iwindi liye kwesokudla"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Khulisa iwindi"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Nciphisa iwindi"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Okokufaka"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Shintshela olimini olulandelayo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Shintshela olimini lwangaphambili"</string>
@@ -1419,7 +1424,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Izilawuli zesistimu"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ama-app esistimu"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ama-app wakamuva"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Hlukanisa isikrini"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
@@ -1428,25 +1432,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Hlela izinqamuleli zekhibhodi ngendlela oyifisayo"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Susa isinqamuleli?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Setha kabusha ubuyele kokuzenzakalelayo?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Cindezela ukhiye ukuze unikeze isinqamuleli"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Lokhu kuzosula isinqamuleli sakho somuntu ngamunye unomphela."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lokhu kuzosula unomphela zonke izinqamuleli zakho zangokwezifiso."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Isithonjana sesenzo noma seMeta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Isithonjana sesengezo"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Enza ngendlela oyifisayo"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Setha kabusha"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kwenziwe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"hlanganisa"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"umugqa otshekele phambili"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setha isinqamuleli"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Susa"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yebo, setha kabusha"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Khansela"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Cindezela ukhiye"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Inhlanganisela yokhiye isiyasetshenziswa kakade. Zama omunye ukhiye."</string>
@@ -1478,6 +1484,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Wenze kahle!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Uqedele ukunyakazisa kokubuka onke ama-app."</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Okopopayi okokufundisa, chofoza ukuze umise kancane futhi uqalise kabusha ukudlala."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a3752640e7ed..28df2e2a1b8c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -124,7 +124,7 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_key_item_color">@*android:color/system_on_surface_variant_light</color>
- <color name="ksh_key_item_background">?androidprv:attr/materialColorSurfaceContainerHighest</color>
+ <color name="ksh_key_item_background">@androidprv:color/materialColorSurfaceContainerHighest</color>
<color name="instant_apps_color">#ff4d5a64</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 478050b0ed85..35cfd082c537 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -237,6 +237,9 @@
<dimen name="status_bar_connected_device_bt_indicator_size">17dp</dimen>
<!-- Height of a small notification in the status bar (2025 redesign version) -->
+ <dimen name="notification_2025_header_height">@*android:dimen/notification_2025_header_height</dimen>
+
+ <!-- Height of a small notification in the status bar (2025 redesign version) -->
<dimen name="notification_2025_min_height">@*android:dimen/notification_2025_min_height</dimen>
<!-- Height of a small notification in the status bar-->
@@ -1221,6 +1224,9 @@
<dimen name="min_window_blur_radius">1px</dimen>
<dimen name="max_window_blur_radius">23px</dimen>
+ <!-- Blur radius behind Notification Shade -->
+ <dimen name="max_shade_window_blur_radius">60dp</dimen>
+
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
@@ -1754,6 +1760,7 @@
<dimen name="wallet_button_vertical_padding">8dp</dimen>
<!-- Ongoing activity chip -->
+ <dimen name="ongoing_activity_chip_max_text_width">74dp</dimen>
<!-- The activity chip side padding, used with the default phone icon. -->
<dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
<!-- The activity chip side padding, used with an icon that has embedded padding (e.g. if the icon comes from the notification's smallIcon field). If the icon has padding, the chip itself can have less padding. -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 88ed4e353719..c54cfe5dbc86 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -280,7 +280,7 @@
<item type="id" name="udfps_accessibility_overlay_top_guideline" />
<!-- Ids for communal hub widgets -->
- <item type="id" name="communal_widget_disposable_tag"/>
+ <item type="id" name="communal_widget_listener_tag"/>
<!-- snapshot view-binding IDs -->
<item type="id" name="snapshot_view_binding" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e417da4d8815..56aaf4c0c564 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1342,6 +1342,8 @@
<string name="glanceable_hub_lockscreen_affordance_disabled_text">To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings.</string>
<!-- Label for a button used to open Settings in order to enable showing widgets on the lock screen. [CHAR LIMIT=NONE] -->
<string name="glanceable_hub_lockscreen_affordance_action_button_label">Settings</string>
+ <!-- Content description for a "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_glanceable_hub_to_dream_button">Show screensaver button</string>
<!-- Related to user switcher --><skip/>
@@ -1509,6 +1511,9 @@
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
+ <!-- Content description for accessibility: Tapping this button will open notifications settings [CHAR LIMIT=NONE] -->
+ <string name="accessibility_notification_section_header_open_settings">Open notifications settings</string>
+
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
<string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
@@ -1812,6 +1817,7 @@
<string name="volume_ringer_change">Tap to change ringer mode</string>
<string name="volume_ringer_mode">ringer mode</string>
+ <string name="volume_ringer_drawer_closed_content_description"><xliff:g id="volume ringer status" example="Ring">%1$s</xliff:g>, tap to change ringer mode </string>
<!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
<string name="volume_ringer_hint_mute">mute</string>
@@ -1944,6 +1950,21 @@
<!-- Text displayed indicating that the user might be able to use satellite SOS. -->
<string name="satellite_emergency_only_carrier_text">Emergency calls or SOS</string>
+ <!-- Content description skeleton. Input strings should be carrier name and signal bar description [CHAR LIMIT=NONE]-->
+ <string name="accessibility_phone_string_format"><xliff:g id="carrier_name" example="Carrier Name">%1$s</xliff:g>, <xliff:g id="signal_strength_description" example="two bars">%2$s</xliff:g>.</string>
+ <!-- Content description describing 0 signal bars. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_no_signal">no signal</string>
+ <!-- Content description describing 1 signal bar. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_one_bar">one bar</string>
+ <!-- Content description describing 2 signal bars. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_two_bars">two bars</string>
+ <!-- Content description describing 3 signal bars. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_three_bars">three bars</string>
+ <!-- Content description describing 4 signal bars. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_four_bars">four bars</string>
+ <!-- Content description describing full signal bars. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_signal_full">signal full</string>
+
<!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
<string name="accessibility_managed_profile">Work profile</string>
@@ -2040,8 +2061,9 @@
<!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
<string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> doesn\u2019t support conversation features</string>
+ <!-- TODO: b/381099727 - Mark translatable once feedback layout is finalized. -->
<!-- [CHAR LIMIT=80] Text shown in feedback button in notification guts for a bundled notification -->
- <string name="notification_guts_bundle_feedback">Provide Bundle Feedback</string>
+ <string name="notification_guts_bundle_feedback" translatable="false">Provide Bundle Feedback</string>
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
@@ -2276,11 +2298,11 @@
<!-- User visible title for the multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_system_multitasking">Multitasking</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app on the right [CHAR LIMIT=70] -->
- <string name="system_multitasking_rhs">Use split screen with current app on the right</string>
+ <string name="system_multitasking_rhs">Use split screen with app on the right</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app on the left [CHAR LIMIT=70] -->
- <string name="system_multitasking_lhs">Use split screen with current app on the left</string>
+ <string name="system_multitasking_lhs">Use split screen with app on the left</string>
<!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
- <string name="system_multitasking_full_screen">Switch from split screen to full screen</string>
+ <string name="system_multitasking_full_screen">Switch to full screen</string>
<!-- User visible title for the keyboard shortcut that switches to app on right or below while using split screen [CHAR LIMIT=70] -->
<string name="system_multitasking_splitscreen_focus_rhs">Switch to app on right or below while using split screen</string>
<!-- User visible title for the keyboard shortcut that switches to app on left or above while using split screen [CHAR LIMIT=70] -->
@@ -2569,8 +2591,6 @@
<!-- Tuner string -->
<!-- Tuner string -->
- <!-- Message shown during shutdown when Find My Device with Dead Battery Finder is active [CHAR LIMIT=300] -->
- <string name="finder_active">You can locate this phone with Find My Device even when powered off</string>
<!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. [CHAR LIMIT=60] -->
<string name="shutdown_progress">Shutting down\u2026</string>
@@ -3912,6 +3932,8 @@
<string name="touchpad_tutorial_recent_apps_gesture_button">View recent apps</string>
<!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_tutorial_done_button">Done</string>
+ <!-- Screen title after gesture was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="gesture_error_title">Try again!</string>
<!-- BACK GESTURE -->
<!-- Touchpad back gesture action name in tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_back_gesture_action_title">Go back</string>
@@ -3921,6 +3943,8 @@
<string name="touchpad_back_gesture_success_title">Nice!</string>
<!-- Text shown to the user after they complete back gesture tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_back_gesture_success_body">You completed the go back gesture.</string>
+ <!-- Text shown to the user after back gesture was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="touchpad_back_gesture_error_body">To go back using your touchpad, swipe left or right using three fingers</string>
<!-- HOME GESTURE -->
<!-- Touchpad home gesture action name in tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_home_gesture_action_title">Go home</string>
@@ -3930,6 +3954,8 @@
<string name="touchpad_home_gesture_success_title">Great job!</string>
<!-- Text shown to the user after they complete home gesture tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_home_gesture_success_body">You completed the go home gesture</string>
+ <!-- Text shown to the user after home gesture was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="touchpad_home_gesture_error_body">Swipe up with three fingers on your touchpad to go to your home screen</string>
<!-- RECENT APPS GESTURE -->
<!-- Touchpad recent apps gesture action name in tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_recent_apps_gesture_action_title">View recent apps</string>
@@ -3939,6 +3965,8 @@
<string name="touchpad_recent_apps_gesture_success_title">Great job!</string>
<!-- Text shown to the user after they complete recent apps gesture tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_recent_apps_gesture_success_body">You completed the view recent apps gesture.</string>
+ <!-- Text shown to the user after recent gesture was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="touchpad_recent_gesture_error_body">To view recent apps, swipe up and hold using three fingers on your touchpad</string>
<!-- KEYBOARD TUTORIAL-->
<!-- Action key tutorial title [CHAR LIMIT=NONE] -->
@@ -3949,6 +3977,11 @@
<string name="tutorial_action_key_success_title">Well done!</string>
<!-- Text shown to the user after they complete action key tutorial [CHAR LIMIT=NONE] -->
<string name="tutorial_action_key_success_body">You completed the view all apps gesture</string>
+ <!-- Text shown to the user after action key tutorial was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="touchpad_action_key_error_body">Press the action key on your keyboard to view all of your apps</string>
+
+ <!-- Content description for the animation playing during the tutorial. The user can click the animation to pause and unpause playback. [CHAR LIMIT=NONE] -->
+ <string name="tutorial_animation_content_description">Tutorial animation, click to pause and resume play.</string>
<!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
<string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9aa71374fb8e..3156a50df96f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -84,6 +84,16 @@
<item name="android:textColor">?android:attr/colorPrimary</item>
</style>
+ <!-- Style for a status bar chip text that has a maximum width. Since there's so little room in
+ the status bar chip area, don't ellipsize the text and instead just fade it out a bit at
+ the end. -->
+ <style name="StatusBar.Chip.Text.LimitedWidth">
+ <item name="android:ellipsize">none</item>
+ <item name="android:requiresFadingEdge">horizontal</item>
+ <item name="android:fadingEdgeLength">@dimen/ongoing_activity_chip_text_fading_edge_length</item>
+ <item name="android:maxWidth">@dimen/ongoing_activity_chip_max_text_width</item>
+ </style>
+
<style name="Chipbar" />
<style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
@@ -151,7 +161,7 @@
<style name="TextAppearance.QS.UserSwitcher">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<!-- This is hard coded to be sans-serif-condensed to match the icons -->
@@ -183,67 +193,46 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
- <style name="TextAppearance.AuthCredential.OldTitle">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:paddingTop">12dp</item>
- <item name="android:paddingHorizontal">24dp</item>
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="TextAppearance.AuthCredential.OldSubtitle">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingHorizontal">24dp</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="TextAppearance.AuthCredential.OldDescription">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingHorizontal">24dp</item>
- <item name="android:textSize">14sp</item>
- </style>
-
<style name="TextAppearance.AuthCredential.LogoDescription" parent="TextAppearance.Material3.LabelLarge" >
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
- <item name="android:gravity">@integer/biometric_dialog_text_gravity</item>
+ <item name="android:gravity">center_horizontal</item>
<item name="android:maxLines">1</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:ellipsize">end</item>
</style>
<style name="TextAppearance.AuthCredential.Title" parent="TextAppearance.Material3.HeadlineSmall" >
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle" parent="TextAppearance.Material3.BodyMedium" >
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthCredential.Description" parent="TextAppearance.Material3.BodyMedium" >
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthCredential.VerticalListContentViewDescription" parent="TextAppearance.Material3.TitleSmall">
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthCredential.ContentViewWithButtonDescription" parent="TextAppearance.AuthCredential.Description" />
<style name="TextAppearance.AuthCredential.ContentViewListItem" parent="TextAppearance.Material3.BodySmall">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:paddingTop">@dimen/biometric_prompt_content_list_item_padding_top</item>
<item name="android:breakStrategy">high_quality</item>
</style>
<style name="TextAppearance.AuthCredential.Indicator" parent="TextAppearance.Material3.BodyMedium">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:marqueeRepeatLimit">marquee_forever</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
@@ -269,21 +258,21 @@
<item name="android:layout_marginTop">24dp</item>
<item name="android:textSize">36dp</item>
<item name="android:focusable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Subtitle">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">20dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Description">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:layout_marginTop">20dp</item>
<item name="android:textSize">18sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.AuthNonBioCredential.Error">
@@ -352,7 +341,7 @@
</style>
<style name="AuthNonCredentialPanelStyle">
- <item name="android:background">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="android:background">@androidprv:color/materialColorSurfaceBright</item>
</style>
<style name="AuthCredentialPanelStyle" parent="AuthNonCredentialPanelStyle">
@@ -383,13 +372,13 @@
<item name="android:minWidth">48dp</item>
<item name="android:paddingLeft">0dp</item>
<item name="android:paddingRight">12dp</item>
- <item name="android:textColor">?androidprv:attr/materialColorPrimary</item>
+ <item name="android:textColor">@androidprv:color/materialColorPrimary</item>
</style>
<style name="AuthCredentialNegativeButtonStyle" parent="TextAppearance.Material3.LabelLarge">
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:background">@color/transparent</item>
- <item name="android:textColor">?androidprv:attr/materialColorPrimary</item>
+ <item name="android:textColor">@androidprv:color/materialColorPrimary</item>
</style>
<style name="DeviceManagementDialogTitle">
@@ -411,7 +400,7 @@
</style>
<style name="KeyboardShortcutHelper.BottomSheet.DragHandle" parent="Widget.Material3.BottomSheet.DragHandle">
- <item name="tint">?androidprv:attr/materialColorOutlineVariant</item>
+ <item name="tint">@androidprv:color/materialColorOutlineVariant</item>
</style>
<style name="KeyboardShortcutHelper.BottomSheetDialogAnimation">
@@ -510,23 +499,23 @@
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
- <item name="surfaceBright">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="surfaceBright">@androidprv:color/materialColorSurfaceBright</item>
<item name="android:colorBackground">?attr/surfaceBright</item>
- <item name="scHigh">?androidprv:attr/materialColorSurfaceContainerHigh</item>
- <item name="primary">?androidprv:attr/materialColorPrimary</item>
- <item name="tertiary">?androidprv:attr/materialColorTertiary</item>
- <item name="onSurface">?androidprv:attr/materialColorOnSurface</item>
- <item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
- <item name="outline">?androidprv:attr/materialColorOutline</item>
-
- <item name="shadeActive">?androidprv:attr/customColorShadeActive</item>
- <item name="onShadeActive">?androidprv:attr/customColorOnShadeActive</item>
- <item name="onShadeActiveVariant">?androidprv:attr/customColorOnShadeActiveVariant</item>
- <item name="shadeInactive">?androidprv:attr/customColorShadeInactive</item>
- <item name="onShadeInactive">?androidprv:attr/customColorOnShadeInactive</item>
- <item name="onShadeInactiveVariant">?androidprv:attr/customColorOnShadeInactiveVariant</item>
- <item name="shadeDisabled">?androidprv:attr/customColorShadeDisabled</item>
- <item name="underSurface">?androidprv:attr/customColorUnderSurface</item>
+ <item name="scHigh">@androidprv:color/materialColorSurfaceContainerHigh</item>
+ <item name="primary">@androidprv:color/materialColorPrimary</item>
+ <item name="tertiary">@androidprv:color/materialColorTertiary</item>
+ <item name="onSurface">@androidprv:color/materialColorOnSurface</item>
+ <item name="onSurfaceVariant">@androidprv:color/materialColorOnSurfaceVariant</item>
+ <item name="outline">@androidprv:color/materialColorOutline</item>
+
+ <item name="shadeActive">@androidprv:color/customColorShadeActive</item>
+ <item name="onShadeActive">@androidprv:color/customColorOnShadeActive</item>
+ <item name="onShadeActiveVariant">@androidprv:color/customColorOnShadeActiveVariant</item>
+ <item name="shadeInactive">@androidprv:color/customColorShadeInactive</item>
+ <item name="onShadeInactive">@androidprv:color/customColorOnShadeInactive</item>
+ <item name="onShadeInactiveVariant">@androidprv:color/customColorOnShadeInactiveVariant</item>
+ <item name="shadeDisabled">@androidprv:color/customColorShadeDisabled</item>
+ <item name="underSurface">@androidprv:color/customColorUnderSurface</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
@@ -588,7 +577,7 @@
<item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
<item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
- <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="android:colorBackground">@androidprv:color/materialColorSurfaceBright</item>
<item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
<item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
@@ -744,34 +733,34 @@
<style name="TextAppearance.NotificationImportanceChannel">
<item name="android:textSize">@dimen/notification_importance_channel_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textSize">@dimen/notification_importance_channel_text</item>
</style>
<style name="TextAppearance.NotificationImportanceChannelGroup">
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
</style>
<style name="TextAppearance.NotificationImportanceApp">
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
</style>
<style name="TextAppearance.NotificationImportanceHeader">
<item name="android:textSize">@dimen/notification_importance_header_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.NotificationImportanceDetail">
<item name="android:textSize">@dimen/notification_importance_description_text</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:gravity">center</item>
</style>
@@ -785,7 +774,7 @@
<style
name="TextAppearance.NotificationSectionHeaderLabel"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:minWidth">0dp</item>
@@ -794,7 +783,7 @@
<style
name="TextAppearance.NotificationSectionHeaderButton"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:minWidth">0dp</item>
@@ -803,7 +792,7 @@
<style
name="TextAppearance.NotificationFooterButton"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:minWidth">0dp</item>
@@ -812,8 +801,8 @@
<style
name="TextAppearance.NotificationFooterButtonRedesign"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
- <item name="android:drawableTint">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">16sp</item>
<item name="android:minWidth">0dp</item>
@@ -1009,12 +998,12 @@
</style>
<style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
- <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item>
+ <item name="android:colorBackground">@androidprv:color/materialColorSurfaceContainer</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar">true</item>
- <item name="android:statusBarColor">?androidprv:attr/materialColorSurfaceContainer</item>
- <item name="android:navigationBarColor">?androidprv:attr/materialColorSurfaceContainerHighest</item>
+ <item name="android:statusBarColor">@androidprv:color/materialColorSurfaceContainer</item>
+ <item name="android:navigationBarColor">@androidprv:color/materialColorSurfaceContainerHighest</item>
<item name="android:windowActivityTransitions">true</item>
</style>
@@ -1092,7 +1081,7 @@
<style name="Theme.VolumePanel.Popup" parent="@style/Theme.SystemUI.Dialog">
<item name="android:dialogCornerRadius">44dp</item>
- <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainerHigh
+ <item name="android:colorBackground">@androidprv:color/materialColorSurfaceContainerHigh
</item>
</style>
@@ -1303,7 +1292,7 @@
</style>
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textSize">@dimen/dialog_title_text_size</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">32sp</item>
@@ -1313,7 +1302,7 @@
</style>
<style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">20sp</item>
@@ -1408,7 +1397,7 @@
<style name="InternetDialog.NetworkTitle.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimaryContainer</item>
</style>
<style name="InternetDialog.NetworkSummary">
@@ -1421,27 +1410,27 @@
<style name="InternetDialog.NetworkSummary.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimaryContainer</item>
</style>
<style name="TextAppearance.InternetDialog">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<style name="TextAppearance.InternetDialog.Active">
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimaryContainer</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary.Active">
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimaryContainer</item>
</style>
<style name="FgsManagerDialogTitle">
@@ -1478,18 +1467,18 @@
<item name="android:orientation">horizontal</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.BluetoothTileDialog">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textDirection">locale</item>
<item name="android:textAlignment">gravity</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.BluetoothTileDialog.Active">
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnPrimaryContainer</item>
</style>
<style name="BluetoothTileDialog.AudioSharingButton" parent="Widget.Dialog.Button">
@@ -1686,7 +1675,7 @@
<style name="ShortCutButton" parent="@android:style/Widget.Material.Button">
<item name="android:background">@drawable/shortcut_button_colored</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:layout_marginEnd">12dp</item>
<item name="android:paddingLeft">24dp</item>
<item name="android:paddingRight">24dp</item>
@@ -1712,18 +1701,18 @@
parent="@android:style/TextAppearance.DeviceDefault.Medium">
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.PrivacyDialog.Item.Summary"
parent="@android:style/TextAppearance.DeviceDefault.Small">
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog">
- <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item>
+ <item name="android:colorBackground">@androidprv:color/materialColorSurfaceContainer</item>
</style>
<style name="Theme.SystemUI.Dialog.StickyKeys" parent="@style/Theme.SystemUI.Dialog">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 9e8cabf141ed..8576a6ebac42 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -330,6 +330,11 @@ public class Task {
}
@Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
public String toString() {
return "[" + key.toString() + "] " + title;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
index 495367b69123..842efa3d68d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -25,7 +25,6 @@ import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.View
import com.android.app.animation.Interpolators
-import com.android.settingslib.Utils
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.TITLE
/** Displays security messages for the keyguard bouncer. */
@@ -71,12 +70,12 @@ open class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
}
override fun onThemeChanged() {
- mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
+ mDefaultColorState = getColorInStyle() ?: ColorStateList.valueOf(context.getColor(TITLE))
super.onThemeChanged()
}
override fun reloadColor() {
- mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
+ mDefaultColorState = getColorInStyle() ?: ColorStateList.valueOf(context.getColor(TITLE))
super.reloadColor()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ec0f582dcb47..0e1eccc8231c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -55,7 +55,6 @@ import com.android.systemui.shared.regionsampling.RegionSampler;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.util.ViewController;
@@ -85,7 +84,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final DumpManager mDumpManager;
private final ClockEventController mClockEventController;
private final LogBuffer mLogBuffer;
- private final NotificationIconContainerAlwaysOnDisplayViewBinder mNicViewBinder;
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -147,7 +145,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
ClockRegistry clockRegistry,
KeyguardSliceViewController keyguardSliceViewController,
LockscreenSmartspaceController smartspaceController,
- NotificationIconContainerAlwaysOnDisplayViewBinder nicViewBinder,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main DelayableExecutor uiExecutor,
@@ -163,7 +160,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockRegistry = clockRegistry;
mKeyguardSliceViewController = keyguardSliceViewController;
mSmartspaceController = smartspaceController;
- mNicViewBinder = nicViewBinder;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
@@ -277,7 +273,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
hideSliceViewAndNotificationIconContainer();
return;
}
- updateAodIcons();
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
mBgExecutor.execute(() -> {
@@ -569,21 +564,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
return mLargeClockFrame.getVisibility() != View.VISIBLE;
}
- private void updateAodIcons() {
- if (!MigrateClocksToBlueprint.isEnabled()) {
- NotificationIconContainer nic = (NotificationIconContainer)
- mView.findViewById(
- com.android.systemui.res.R.id.left_aligned_notification_icon_container);
- if (mAodIconsBindHandle != null) {
- mAodIconsBindHandle.dispose();
- }
- if (nic != null) {
- mAodIconsBindHandle = mNicViewBinder.bindWhileAttached(nic);
- mAodIconContainer = nic;
- }
- }
- }
-
private void setClock(ClockController clock) {
if (MigrateClocksToBlueprint.isEnabled()) {
return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index fcaccd27a567..63d4fe3f1b01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -51,6 +51,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
@@ -97,6 +98,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
@@ -346,8 +348,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
setPadding(getPaddingLeft(), getPaddingTop() + getResources().getDimensionPixelSize(
R.dimen.keyguard_security_container_padding_top), getPaddingRight(),
getPaddingBottom());
- setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.materialColorSurfaceDim));
+ reloadBackgroundColor();
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
@@ -812,10 +813,20 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
mDisappearAnimRunning = false;
}
+ private void reloadBackgroundColor() {
+ if (Flags.bouncerUiRevamp()) {
+ // Keep the background transparent, otherwise the background color looks like a box
+ // while scaling the bouncer for back animation or while transitioning to the bouncer.
+ setBackgroundColor(Color.TRANSPARENT);
+ } else {
+ setBackgroundColor(
+ getContext().getColor(com.android.internal.R.color.materialColorSurfaceDim));
+ }
+ }
+
void reloadColors() {
mViewMode.reloadColors();
- setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.materialColorSurfaceDim));
+ reloadBackgroundColor();
}
/** Handles density or font scale changes. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
index 392abf2aa82c..0942353a7110 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -20,7 +20,6 @@ import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import androidx.core.graphics.drawable.DrawableCompat
-import com.android.settingslib.Utils
import com.android.systemui.res.R
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.EMERGENCY_BUTTON
@@ -45,7 +44,7 @@ abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) :
override fun reloadColors() {
super.reloadColors()
- val imageColor = Utils.getColorAttrDefaultColor(context, EMERGENCY_BUTTON)
+ val imageColor = context.getColor(EMERGENCY_BUTTON)
simImageView?.let {
val wrappedDrawable = DrawableCompat.wrap(it.drawable)
DrawableCompat.setTint(wrappedDrawable, imageColor)
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index e77341651a8e..dccf53a369ac 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -15,13 +15,11 @@
*/
package com.android.keyguard;
-import static com.android.settingslib.Utils.getColorAttrDefaultColor;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND_PRESSED;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_PRESSED;
-import static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
@@ -127,17 +125,18 @@ class NumPadAnimator {
int[] customAttrs = {android.R.attr.colorControlNormal};
ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
@SuppressLint("ResourceType") TypedArray a = ctw.obtainStyledAttributes(customAttrs);
- mNormalBackgroundColor = getPrivateAttrColorIfUnset(ctw, a, 0, 0,
- NUM_PAD_BACKGROUND);
+
+ mNormalBackgroundColor = a.getColor(0, context.getColor(NUM_PAD_BACKGROUND));
+
a.recycle();
- mPressedBackgroundColor = getColorAttrDefaultColor(context, NUM_PAD_BACKGROUND_PRESSED);
- mTextColorPressed = getColorAttrDefaultColor(context, NUM_PAD_PRESSED);
+ mPressedBackgroundColor = context.getColor(NUM_PAD_BACKGROUND_PRESSED);
+ mTextColorPressed = context.getColor(NUM_PAD_PRESSED);
mBackground.setColor(mNormalBackgroundColor);
mTextColorPrimary = isNumPadKey
- ? getColorAttrDefaultColor(context, NUM_PAD_KEY)
- : getColorAttrDefaultColor(context, NUM_PAD_BUTTON);
+ ? context.getColor(NUM_PAD_KEY)
+ : context.getColor(NUM_PAD_BUTTON);
createAnimators();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index a81c1b0bf9c3..d7799bf505bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -30,7 +30,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
-import com.android.settingslib.Utils;
import com.android.systemui.res.R;
/**
@@ -101,7 +100,7 @@ public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAni
if (mAnimator != null) mAnimator.reloadColors(getContext());
int textColorResId = mIsTransparentMode ? NUM_PAD_KEY : NUM_PAD_BUTTON;
- int imageColor = Utils.getColorAttrDefaultColor(getContext(), textColorResId);
+ int imageColor = getContext().getColor(textColorResId);
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index ebde8a3057ce..e8a702f5fca3 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -155,8 +155,7 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
* Reload colors from resources.
**/
public void reloadColors() {
- int textColor = Utils.getColorAttr(getContext(), NUM_PAD_KEY)
- .getDefaultColor();
+ int textColor = getContext().getColor(NUM_PAD_KEY);
int klondikeColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary)
.getDefaultColor();
mDigitText.setTextColor(textColor);
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
index 5e9eed98f97b..bac9dacef9e1 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
@@ -28,7 +28,6 @@ import android.widget.LinearLayout;
import androidx.core.graphics.drawable.DrawableCompat;
-import com.android.settingslib.Utils;
import com.android.systemui.res.R;
/**
@@ -39,8 +38,7 @@ public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
private int mPinLength;
private int mDotDiameter;
- private int mColor = Utils.getColorAttr(getContext(), PIN_SHAPES)
- .getDefaultColor();
+ private int mColor = getContext().getColor(PIN_SHAPES);
private int mPosition = 0;
private static final int DEFAULT_PIN_LENGTH = 6;
private PinShapeAdapter mPinShapeAdapter;
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
index ee70de3e86a0..26a774e991a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
@@ -39,7 +39,6 @@ import android.widget.LinearLayout;
import androidx.core.graphics.drawable.DrawableCompat;
import com.android.app.animation.Interpolators;
-import com.android.settingslib.Utils;
import com.android.systemui.res.R;
/**
@@ -49,7 +48,7 @@ import com.android.systemui.res.R;
public class PinShapeNonHintingView extends LinearLayout implements PinShapeInput {
private static final int RESET_STAGGER_DELAY = 40;
private static final int RESET_MAX_DELAY = 200;
- private int mColor = Utils.getColorAttr(getContext(), PIN_SHAPES).getDefaultColor();
+ private int mColor = getContext().getColor(PIN_SHAPES);
private int mPosition = 0;
private boolean mIsAnimatingReset = false;
private final PinShapeAdapter mPinShapeAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
index 08236b7e280a..ca5424bc0c52 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
+++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.systemui;
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index 3abcb139ab5c..11ce168b9bcb 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -32,7 +32,6 @@ import android.view.View
import androidx.core.graphics.ColorUtils
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.settingslib.Utils
import com.android.systemui.biometrics.AuthController
import com.android.systemui.log.ScreenDecorationsLogger
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -60,8 +59,8 @@ class FaceScanningOverlay(
private val rimRect = RectF()
private var cameraProtectionColor = Color.BLACK
- var faceScanningAnimColor = Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.materialColorPrimaryFixed)
+ var faceScanningAnimColor =
+ context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
private var cameraProtectionAnimator: ValueAnimator? = null
var hideOverlayRunnable: Runnable? = null
diff --git a/packages/SystemUI/src/com/android/systemui/FontStyles.kt b/packages/SystemUI/src/com/android/systemui/FontStyles.kt
new file mode 100644
index 000000000000..d8cd6c87a1ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/FontStyles.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+/** String tokens for the different GSF font families. */
+object FontStyles {
+
+ const val GSF_LABEL_MEDIUM = "gsf-label-medium"
+ const val GSF_LABEL_LARGE = "gsf-label-large"
+
+ const val GSF_BODY_MEDIUM = "gsf-body-medium"
+
+ const val GSF_TITLE_SMALL_EMPHASIZED = "gsf-title-small-emphasized"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index a46b236d46fb..981732278acd 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -46,6 +46,8 @@ import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.ConfigurationForwarder;
import com.android.systemui.util.NotificationChannels;
+import com.android.wm.shell.dagger.HasWMComponent;
+import com.android.wm.shell.dagger.WMComponent;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
@@ -62,7 +64,7 @@ import javax.inject.Provider;
* Application class for SystemUI.
*/
public class SystemUIApplication extends Application implements
- SystemUIAppComponentFactoryBase.ContextInitializer {
+ SystemUIAppComponentFactoryBase.ContextInitializer, HasWMComponent {
public static final String TAG = "SystemUIService";
private static final boolean DEBUG = false;
@@ -490,4 +492,10 @@ public class SystemUIApplication extends Application implements
n.addExtras(extras);
}
+
+ @NonNull
+ @Override
+ public WMComponent getWMComponent() {
+ return mInitializer.getWMComponent();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 5c75a49818a6..f530522fb707 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -24,9 +24,9 @@ import android.util.Log;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
-import com.android.systemui.dagger.WMComponent;
import com.android.systemui.res.R;
import com.android.systemui.util.InitializationChecker;
+import com.android.wm.shell.dagger.WMComponent;
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.shared.ShellTransitions;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
index 1f66c91b3573..1ed8c068f974 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 44215
-include /core/java/android/view/accessibility/OWNERS \ No newline at end of file
+include /core/java/android/view/accessibility/OWNERS
+jonesriley@google.com \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 559e6f767f7b..ffb5f3d47bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
+import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
@@ -42,6 +43,7 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -75,6 +77,9 @@ class MenuInfoRepository {
private final Context mContext;
private final Configuration mConfiguration;
+ private final AccessibilityManager mAccessibilityManager;
+ private final AccessibilityManager.AccessibilityServicesStateChangeListener
+ mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final OnSettingsContentsChanged mSettingsContentsCallback;
private final SecureSettings mSecureSettings;
@@ -142,9 +147,10 @@ class MenuInfoRepository {
}
};
- MenuInfoRepository(Context context,
+ MenuInfoRepository(Context context, AccessibilityManager accessibilityManager,
OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) {
mContext = context;
+ mAccessibilityManager = accessibilityManager;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mSettingsContentsCallback = settingsContentsChanged;
mSecureSettings = secureSettings;
@@ -238,6 +244,13 @@ class MenuInfoRepository {
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mSecureSettings.registerContentObserverForUserSync(
+ mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
+ /* notifyForDescendants */ false,
+ mMenuTargetFeaturesContentObserver,
+ UserHandle.USER_CURRENT);
+ }
mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
@@ -251,6 +264,11 @@ class MenuInfoRepository {
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
+
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mAccessibilityManager.addAccessibilityServicesStateChangeListener(
+ mA11yServicesStateChangeListener);
+ }
}
void unregisterObserversAndCallbacks() {
@@ -258,6 +276,11 @@ class MenuInfoRepository {
mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
+
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
+ mA11yServicesStateChangeListener);
+ }
}
interface OnSettingsContentsChanged {
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 cfcaa4fea99b..cb96e7859fba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -42,7 +42,8 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu {
NavigationModeController navigationModeController) {
mWindowManager = viewCaptureAwareWindowManager;
- MenuViewModel menuViewModel = new MenuViewModel(context, secureSettings);
+ MenuViewModel menuViewModel = new MenuViewModel(
+ context, accessibilityManager, secureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index 46c407e24fe2..f924784a5535 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.floatingmenu;
import android.content.Context;
+import android.view.accessibility.AccessibilityManager;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@@ -42,9 +43,10 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
private final MenuInfoRepository mInfoRepository;
- MenuViewModel(Context context, SecureSettings secureSettings) {
- mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this,
- secureSettings);
+ MenuViewModel(Context context, AccessibilityManager accessibilityManager,
+ SecureSettings secureSettings) {
+ mInfoRepository = new MenuInfoRepository(context,
+ accessibilityManager, /* settingsContentsChanged= */ this, secureSettings);
}
@Override
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 1f21af80cebb..56435df1ad2c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -52,10 +52,8 @@ import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback;
@@ -67,7 +65,6 @@ import com.android.systemui.bluetooth.qsdialog.DeviceItem;
import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory;
import com.android.systemui.bluetooth.qsdialog.DeviceItemType;
import com.android.systemui.bluetooth.qsdialog.SavedHearingDeviceItemFactory;
-import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
@@ -87,6 +84,7 @@ import java.util.stream.Collectors;
*/
public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
HearingDeviceItemCallback, BluetoothCallback {
+
private static final String TAG = "HearingDevicesDialogDelegate";
@VisibleForTesting
static final String ACTION_BLUETOOTH_DEVICE_DETAILS =
@@ -96,25 +94,27 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
@VisibleForTesting
static final Intent LIVE_CAPTION_INTENT = new Intent(
"com.android.settings.action.live_caption");
+
private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final DialogTransitionAnimator mDialogTransitionAnimator;
private final ActivityStarter mActivityStarter;
- private final boolean mShowPairNewDevice;
private final LocalBluetoothManager mLocalBluetoothManager;
private final Handler mMainHandler;
private final AudioManager mAudioManager;
private final LocalBluetoothProfileManager mProfileManager;
- private final HapClientProfile mHapClientProfile;
private final HearingDevicesUiEventLogger mUiEventLogger;
+ private final boolean mShowPairNewDevice;
private final int mLaunchSourceId;
- private HearingDevicesListAdapter mDeviceListAdapter;
- private HearingDevicesPresetsController mPresetsController;
- private Context mApplicationContext;
+
private SystemUIDialog mDialog;
+
private RecyclerView mDeviceList;
private List<DeviceItem> mHearingDeviceItemList;
+ private HearingDevicesListAdapter mDeviceListAdapter;
+
private View mPresetLayout;
private Spinner mPresetSpinner;
+ private HearingDevicesPresetsController mPresetController;
private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
new HearingDevicesPresetsController.PresetCallback() {
@@ -122,20 +122,18 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
public void onPresetInfoUpdated(List<BluetoothHapPresetInfo> presetInfos,
int activePresetIndex) {
mMainHandler.post(
- () -> refreshPresetInfoAdapter(presetInfos, activePresetIndex));
+ () -> refreshPresetUi(presetInfos, activePresetIndex));
}
@Override
public void onPresetCommandFailed(int reason) {
- final List<BluetoothHapPresetInfo> presetInfos =
- mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
+ mPresetController.refreshPresetInfo();
mMainHandler.post(() -> {
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
- showPresetErrorToast(mApplicationContext);
+ showErrorToast(R.string.hearing_devices_presets_error);
});
}
};
+
private final List<DeviceItemFactory> mHearingDeviceItemFactoryList = List.of(
new ActiveHearingDeviceItemFactory(),
new AvailableHearingDeviceItemFactory(),
@@ -159,7 +157,6 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
@AssistedInject
public HearingDevicesDialogDelegate(
- @Application Context applicationContext,
@Assisted boolean showPairNewDevice,
@Assisted @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId,
SystemUIDialog.Factory systemUIDialogFactory,
@@ -169,7 +166,6 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
@Main Handler handler,
AudioManager audioManager,
HearingDevicesUiEventLogger uiEventLogger) {
- mApplicationContext = applicationContext;
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
mActivityStarter = activityStarter;
@@ -178,7 +174,6 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mMainHandler = handler;
mAudioManager = audioManager;
mProfileManager = localBluetoothManager.getProfileManager();
- mHapClientProfile = mProfileManager.getHapClientProfile();
mUiEventLogger = uiEventLogger;
mLaunchSourceId = launchSourceId;
}
@@ -229,38 +224,26 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
@Override
public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
- CachedBluetoothDevice activeHearingDevice;
- mHearingDeviceItemList = getHearingDevicesList();
- if (mPresetsController != null) {
- activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList);
- mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
- } else {
- activeHearingDevice = null;
+ refreshDeviceUi();
+ if (mPresetController != null) {
+ mPresetController.setDevice(getActiveHearingDevice());
+ mMainHandler.post(() -> {
+ mPresetLayout.setVisibility(
+ mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
+ });
}
- mMainHandler.post(() -> {
- mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList);
- final List<BluetoothHapPresetInfo> presetInfos =
- mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
- mPresetLayout.setVisibility(
- (activeHearingDevice != null && activeHearingDevice.isConnectedHapClientDevice()
- && !mPresetInfoAdapter.isEmpty()) ? VISIBLE : GONE);
- });
}
@Override
public void onProfileConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
int state, int bluetoothProfile) {
- mHearingDeviceItemList = getHearingDevicesList();
- mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList));
+ refreshDeviceUi();
}
@Override
public void onAclConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
int state) {
- mHearingDeviceItemList = getHearingDevicesList();
- mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList));
+ refreshDeviceUi();
}
@Override
@@ -306,13 +289,9 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
if (mLocalBluetoothManager == null) {
return;
}
-
mLocalBluetoothManager.getEventManager().registerCallback(this);
- if (mPresetsController != null) {
- mPresetsController.registerHapCallback();
- if (mHapClientProfile != null && !mHapClientProfile.isProfileReady()) {
- mProfileManager.addServiceListener(mPresetsController);
- }
+ if (mPresetController != null) {
+ mPresetController.registerHapCallback();
}
}
@@ -322,37 +301,25 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
return;
}
- if (mPresetsController != null) {
- mPresetsController.unregisterHapCallback();
- mProfileManager.removeServiceListener(mPresetsController);
+ if (mPresetController != null) {
+ mPresetController.unregisterHapCallback();
}
mLocalBluetoothManager.getEventManager().unregisterCallback(this);
}
- @VisibleForTesting
- void setHearingDevicesPresetsController(HearingDevicesPresetsController controller) {
- mPresetsController = controller;
- }
-
private void setupDeviceListView(SystemUIDialog dialog) {
mDeviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext()));
- mHearingDeviceItemList = getHearingDevicesList();
+ mHearingDeviceItemList = getHearingDeviceItemList();
mDeviceListAdapter = new HearingDevicesListAdapter(mHearingDeviceItemList, this);
mDeviceList.setAdapter(mDeviceListAdapter);
}
private void setupPresetSpinner(SystemUIDialog dialog) {
- if (mPresetsController == null) {
- mPresetsController = new HearingDevicesPresetsController(mProfileManager,
- mPresetCallback);
- }
- final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
- mHearingDeviceItemList);
- mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
+ mPresetController = new HearingDevicesPresetsController(mProfileManager, mPresetCallback);
+ mPresetController.setDevice(getActiveHearingDevice());
mPresetInfoAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
mPresetSpinner.setAdapter(mPresetInfoAdapter);
-
// disable redundant Touch & Hold accessibility action for Switch Access
mPresetSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -362,20 +329,18 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
super.onInitializeAccessibilityNodeInfo(host, info);
}
});
-
- // Refresh the spinner and setSelection(index, false) before setOnItemSelectedListener() to
- // avoid extra onItemSelected() get called when first register the listener.
- final List<BluetoothHapPresetInfo> presetInfos = mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
+ // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+ // to avoid extra onItemSelected() get called when first register the listener.
+ refreshPresetUi(mPresetController.getAllPresetInfo(),
+ mPresetController.getActivePresetIndex());
mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mPresetInfoAdapter.setSelected(position);
mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT,
mLaunchSourceId);
- mPresetsController.selectPreset(
- mPresetsController.getAllPresetInfo().get(position).getIndex());
+ mPresetController.selectPreset(
+ mPresetController.getAllPresetInfo().get(position).getIndex());
}
@Override
@@ -383,9 +348,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
// Do nothing
}
});
- mPresetLayout.setVisibility(
- (activeHearingDevice != null && activeHearingDevice.isConnectedHapClientDevice()
- && !mPresetInfoAdapter.isEmpty()) ? VISIBLE : GONE);
+
+ mPresetLayout.setVisibility(mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
}
private void setupPairNewDeviceButton(SystemUIDialog dialog) {
@@ -405,13 +369,12 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
private void setupRelatedToolsView(SystemUIDialog dialog) {
-
final Context context = dialog.getContext();
final List<ToolItem> toolItemList = new ArrayList<>();
final String[] toolNameArray;
final String[] toolIconArray;
- ToolItem preInstalledItem = getLiveCaption(context);
+ ToolItem preInstalledItem = getLiveCaptionToolItem(context);
if (preInstalledItem != null) {
toolItemList.add(preInstalledItem);
}
@@ -432,7 +395,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
final LinearLayout toolsContainer = dialog.requireViewById(R.id.tools_container);
for (int i = 0; i < toolItemList.size(); i++) {
- View view = createHearingToolView(context, toolItemList.get(i), toolsContainer);
+ View view = createToolView(context, toolItemList.get(i), toolsContainer);
toolsContainer.addView(view);
if (i != toolItemList.size() - 1) {
final int spaceSize = context.getResources().getDimensionPixelSize(
@@ -444,8 +407,14 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
}
- private void refreshPresetInfoAdapter(List<BluetoothHapPresetInfo> presetInfos,
- int activePresetIndex) {
+ private void refreshDeviceUi() {
+ mHearingDeviceItemList = getHearingDeviceItemList();
+ mMainHandler.post(() -> {
+ mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList);
+ });
+ }
+
+ private void refreshPresetUi(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex) {
mPresetInfoAdapter.clear();
mPresetInfoAdapter.addAll(
presetInfos.stream().map(BluetoothHapPresetInfo::getName).toList());
@@ -460,12 +429,11 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
}
- private List<DeviceItem> getHearingDevicesList() {
+ private List<DeviceItem> getHearingDeviceItemList() {
if (mLocalBluetoothManager == null
|| !mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) {
return emptyList();
}
-
return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream()
.map(this::createHearingDeviceItem)
.filter(Objects::nonNull)
@@ -473,8 +441,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
@Nullable
- private CachedBluetoothDevice getActiveHearingDevice(List<DeviceItem> hearingDeviceItemList) {
- return hearingDeviceItemList.stream()
+ private CachedBluetoothDevice getActiveHearingDevice() {
+ return mHearingDeviceItemList.stream()
.filter(item -> item.getType() == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
.map(DeviceItem::getCachedBluetoothDevice)
.findFirst()
@@ -495,7 +463,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
@NonNull
- private View createHearingToolView(Context context, ToolItem item, ViewGroup container) {
+ private View createToolView(Context context, ToolItem item, ViewGroup container) {
View view = LayoutInflater.from(context).inflate(R.layout.hearing_tool_item, container,
false);
ImageView icon = view.requireViewById(R.id.tool_icon);
@@ -503,8 +471,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
view.setContentDescription(item.getToolName());
icon.setImageDrawable(item.getToolIcon());
if (item.isCustomIcon()) {
- icon.getDrawable().mutate().setTint(Utils.getColorAttr(context,
- com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor());
+ icon.getDrawable().mutate().setTint(context.getColor(
+ com.android.internal.R.color.materialColorOnPrimaryContainer));
}
text.setText(item.getToolName());
Intent intent = item.getToolIntent();
@@ -522,7 +490,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
return view;
}
- private ToolItem getLiveCaption(Context context) {
+ private ToolItem getLiveCaptionToolItem(Context context) {
final PackageManager packageManager = context.getPackageManager();
LIVE_CAPTION_INTENT.setPackage(packageManager.getSystemCaptionsServicePackageName());
final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
@@ -534,7 +502,6 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
LIVE_CAPTION_INTENT,
/* isCustomIcon= */ true);
}
-
return null;
}
@@ -544,7 +511,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
}
- private void showPresetErrorToast(Context context) {
- Toast.makeText(context, R.string.hearing_devices_presets_error, Toast.LENGTH_SHORT).show();
+ private void showErrorToast(int stringResId) {
+ Toast.makeText(mDialog.getContext(), stringResId, Toast.LENGTH_SHORT).show();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
index e47e4b27d58a..7e1d538a80ee 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
@@ -27,7 +27,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.settingslib.Utils;
import com.android.systemui.bluetooth.qsdialog.DeviceItem;
import com.android.systemui.res.R;
@@ -131,10 +130,9 @@ public class HearingDevicesListAdapter extends RecyclerView.Adapter<RecyclerView
}
// tint different color in different state for bad color contrast problem
- int tintColor = item.isActive() ? Utils.getColorAttr(mContext,
- com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor()
- : Utils.getColorAttr(mContext,
- com.android.internal.R.attr.materialColorOnSurface).getDefaultColor();
+ int tintColor = item.isActive() ? mContext.getColor(
+ com.android.internal.R.color.materialColorOnPrimaryContainer)
+ : mContext.getColor(com.android.internal.R.color.materialColorOnSurface);
Pair<Drawable, String> iconPair = item.getIconWithDescription();
if (iconPair != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index aa95fd038260..e109108d7df5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -25,16 +25,18 @@ import android.bluetooth.BluetoothHapPresetInfo;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.utils.ThreadUtils;
+import java.util.ArrayList;
import java.util.List;
/**
- * The controller of the hearing devices presets of the bluetooth Hearing Access Profile.
+ * The controller of handling hearing device preset with Bluetooth Hearing Access Profile(HAP).
*/
public class HearingDevicesPresetsController implements
LocalBluetoothProfileManager.ServiceListener, BluetoothHapClient.Callback {
@@ -46,11 +48,13 @@ public class HearingDevicesPresetsController implements
private final HapClientProfile mHapClientProfile;
private final PresetCallback mPresetCallback;
- private CachedBluetoothDevice mActiveHearingDevice;
+ private CachedBluetoothDevice mDevice;
+ private List<BluetoothHapPresetInfo> mPresetInfos = new ArrayList<>();
+ private int mActivePresetIndex = BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
private int mSelectedPresetIndex;
- public HearingDevicesPresetsController(LocalBluetoothProfileManager profileManager,
- PresetCallback presetCallback) {
+ public HearingDevicesPresetsController(@NonNull LocalBluetoothProfileManager profileManager,
+ @Nullable PresetCallback presetCallback) {
mProfileManager = profileManager;
mHapClientProfile = mProfileManager.getHapClientProfile();
mPresetCallback = presetCallback;
@@ -61,7 +65,7 @@ public class HearingDevicesPresetsController implements
if (mHapClientProfile != null && mHapClientProfile.isProfileReady()) {
mProfileManager.removeServiceListener(this);
registerHapCallback();
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@@ -72,51 +76,53 @@ public class HearingDevicesPresetsController implements
@Override
public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
if (DEBUG) {
Log.d(TAG, "onPresetSelected, device: " + device.getAddress()
+ ", presetIndex: " + presetIndex + ", reason: " + reason);
}
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@Override
public void onPresetInfoChanged(@NonNull BluetoothDevice device,
@NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
if (DEBUG) {
Log.d(TAG, "onPresetInfoChanged, device: " + device.getAddress()
+ ", reason: " + reason + ", infoList: " + presetInfoList);
}
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@Override
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
Log.w(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
+ ", reason: " + reason);
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
}
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
- if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
+ if (hapGroupId == mHapClientProfile.getHapGroup(mDevice.getDevice())) {
Log.w(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
+ ", reason: " + reason);
selectPresetIndependently(mSelectedPresetIndex);
@@ -125,33 +131,43 @@ public class HearingDevicesPresetsController implements
@Override
public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
Log.w(TAG, "onSetPresetNameFailed, device: " + device.getAddress()
+ ", reason: " + reason);
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
}
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
- if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
+ if (hapGroupId == mHapClientProfile.getHapGroup(mDevice.getDevice())) {
Log.w(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
+ ", reason: " + reason);
}
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
/**
- * Registers a callback to be notified about operation changed for {@link HapClientProfile}.
+ * Registers a callback to be notified about operation changed of {@link HapClientProfile}.
*/
public void registerHapCallback() {
if (mHapClientProfile != null) {
+ if (!mHapClientProfile.isProfileReady()) {
+ mProfileManager.addServiceListener(this);
+ Log.w(TAG, "Profile is not ready yet, the callback will be registered once the "
+ + "profile is ready.");
+ return;
+ }
try {
mHapClientProfile.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
} catch (IllegalArgumentException e) {
@@ -163,9 +179,10 @@ public class HearingDevicesPresetsController implements
}
/**
- * Removes a previously-added {@link HapClientProfile} callback.
+ * Removes a previously-added {@link HapClientProfile} callback if exist.
*/
public void unregisterHapCallback() {
+ mProfileManager.removeServiceListener(this);
if (mHapClientProfile != null) {
try {
mHapClientProfile.unregisterCallback(this);
@@ -177,108 +194,137 @@ public class HearingDevicesPresetsController implements
}
/**
- * Sets the hearing device for this controller to control the preset if it supports
- * {@link HapClientProfile}.
+ * Sets the device for this controller to control the preset if it supports
+ * {@link HapClientProfile}, otherwise the device of this controller will be {@code null}.
*
- * @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device
- * and support {@link HapClientProfile}.
+ * @param device the {@link CachedBluetoothDevice} set to the controller
*/
- public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) {
- if (mHapClientProfile == null || activeHearingDevice == null) {
- mActiveHearingDevice = null;
- return;
- }
- if (activeHearingDevice.getProfiles().stream().anyMatch(
+ public void setDevice(@Nullable CachedBluetoothDevice device) {
+ if (device != null && device.getProfiles().stream().anyMatch(
profile -> profile instanceof HapClientProfile)) {
- mActiveHearingDevice = activeHearingDevice;
+ mDevice = device;
} else {
- mActiveHearingDevice = null;
+ mDevice = null;
}
+ refreshPresetInfo();
}
/**
- * Selects the currently active preset for {@code mActiveHearingDevice} individual device or
- * the device group according to whether it supports synchronized presets or not.
+ * Refreshes the preset info of {@code mDevice}. If the preset info list or the active preset
+ * index is updated, the {@link PresetCallback#onPresetInfoUpdated(List, int)} will be called
+ * to notify the change.
*
- * @param presetIndex an index of one of the available presets
+ * <b>Note:</b> If {@code mDevice} is null, the cached preset info and active preset index will
+ * be reset to empty list and {@code BluetoothHapClient.PRESET_INDEX_UNAVAILABLE} respectively.
*/
- public void selectPreset(int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
- return;
+ public void refreshPresetInfo() {
+ List<BluetoothHapPresetInfo> updatedInfos = new ArrayList<>();
+ int updatedActiveIndex = BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+ if (mHapClientProfile != null && mDevice != null) {
+ updatedInfos = mHapClientProfile.getAllPresetInfo(mDevice.getDevice()).stream().filter(
+ BluetoothHapPresetInfo::isAvailable).toList();
+ updatedActiveIndex = mHapClientProfile.getActivePresetIndex(mDevice.getDevice());
}
- mSelectedPresetIndex = presetIndex;
- boolean supportSynchronizedPresets = mHapClientProfile.supportsSynchronizedPresets(
- mActiveHearingDevice.getDevice());
- int hapGroupId = mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice());
- if (supportSynchronizedPresets) {
- if (hapGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
- selectPresetSynchronously(hapGroupId, presetIndex);
- } else {
- Log.w(TAG, "supportSynchronizedPresets but hapGroupId is invalid.");
- selectPresetIndependently(presetIndex);
+ final boolean infoUpdated = !mPresetInfos.equals(updatedInfos);
+ final boolean activeIndexUpdated = mActivePresetIndex != updatedActiveIndex;
+ mPresetInfos = updatedInfos;
+ mActivePresetIndex = updatedActiveIndex;
+ if (infoUpdated || activeIndexUpdated) {
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetInfoUpdated(mPresetInfos, mActivePresetIndex);
}
- } else {
- selectPresetIndependently(presetIndex);
}
}
/**
- * Gets all preset info for {@code mActiveHearingDevice} device.
- *
- * @return a list of all known preset info
+ * @return if the preset control is available. The preset control is available only
+ * when the {@code mDevice} supports HAP and the retrieved preset info list is not empty.
+ */
+ public boolean isPresetControlAvailable() {
+ boolean deviceValid = mDevice != null && mDevice.isConnectedHapClientDevice();
+ boolean hasPreset = mPresetInfos != null && !mPresetInfos.isEmpty();
+ return deviceValid && hasPreset;
+ }
+
+ /**
+ * @return a list of {@link BluetoothHapPresetInfo} retrieved from {@code mDevice}
*/
public List<BluetoothHapPresetInfo> getAllPresetInfo() {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return emptyList();
}
- return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
- BluetoothHapPresetInfo::isAvailable).toList();
+ return mPresetInfos;
}
/**
- * Gets the currently active preset for {@code mActiveHearingDevice} device.
+ * Gets the currently active preset of {@code mDevice}.
*
* @return active preset index
*/
public int getActivePresetIndex() {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
}
- return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice());
+ return mActivePresetIndex;
+ }
+
+ /**
+ * Selects the preset for {@code mDevice}. Performs individual or group operation according
+ * to whether the device supports synchronized presets feature or not.
+ *
+ * @param presetIndex an index of one of the available presets
+ */
+ public void selectPreset(int presetIndex) {
+ if (mDevice == null || mHapClientProfile == null) {
+ return;
+ }
+ mSelectedPresetIndex = presetIndex;
+ boolean supportSynchronizedPresets = mHapClientProfile.supportsSynchronizedPresets(
+ mDevice.getDevice());
+ int hapGroupId = mHapClientProfile.getHapGroup(mDevice.getDevice());
+ if (supportSynchronizedPresets) {
+ if (hapGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ selectPresetSynchronously(hapGroupId, presetIndex);
+ } else {
+ Log.w(TAG, "supportSynchronizedPresets but hapGroupId is invalid.");
+ selectPresetIndependently(presetIndex);
+ }
+ } else {
+ selectPresetIndependently(presetIndex);
+ }
}
private void selectPresetSynchronously(int groupId, int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
Log.d(TAG, "selectPresetSynchronously"
+ ", presetIndex: " + presetIndex
+ ", groupId: " + groupId
- + ", device: " + mActiveHearingDevice.getAddress());
+ + ", device: " + mDevice.getAddress());
}
mHapClientProfile.selectPresetForGroup(groupId, presetIndex);
}
private void selectPresetIndependently(int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
Log.d(TAG, "selectPresetIndependently"
+ ", presetIndex: " + presetIndex
- + ", device: " + mActiveHearingDevice.getAddress());
+ + ", device: " + mDevice.getAddress());
}
- mHapClientProfile.selectPreset(mActiveHearingDevice.getDevice(), presetIndex);
- final CachedBluetoothDevice subDevice = mActiveHearingDevice.getSubDevice();
+ mHapClientProfile.selectPreset(mDevice.getDevice(), presetIndex);
+ final CachedBluetoothDevice subDevice = mDevice.getSubDevice();
if (subDevice != null) {
if (DEBUG) {
Log.d(TAG, "selectPreset for subDevice, device: " + subDevice);
}
mHapClientProfile.selectPreset(subDevice.getDevice(), presetIndex);
}
- for (final CachedBluetoothDevice memberDevice :
- mActiveHearingDevice.getMemberDevice()) {
+ for (final CachedBluetoothDevice memberDevice : mDevice.getMemberDevice()) {
if (DEBUG) {
Log.d(TAG, "selectPreset for memberDevice, device: " + memberDevice);
}
@@ -294,9 +340,8 @@ public class HearingDevicesPresetsController implements
/**
* Called when preset info from {@link HapClientProfile} operation get updated.
*
- * @param presetInfos all preset info for {@code mActiveHearingDevice} device
- * @param activePresetIndex currently active preset index for {@code mActiveHearingDevice}
- * device
+ * @param presetInfos all preset info of {@code mDevice}
+ * @param activePresetIndex currently active preset index of {@code mDevice}
*/
void onPresetInfoUpdated(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesSpinnerAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesSpinnerAdapter.java
index 28d742cfa49b..fda4f0385f55 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesSpinnerAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesSpinnerAdapter.java
@@ -64,9 +64,9 @@ public class HearingDevicesSpinnerAdapter extends ArrayAdapter<String> {
TextView text = view.findViewById(R.id.hearing_devices_spinner_text);
if (text != null) {
- int tintColor = Utils.getColorAttr(mContext,
- isSelected ? com.android.internal.R.attr.materialColorOnPrimaryContainer
- : com.android.internal.R.attr.materialColorOnSurface).getDefaultColor();
+ int tintColor = mContext.getColor(
+ isSelected ? com.android.internal.R.color.materialColorOnPrimaryContainer
+ : com.android.internal.R.color.materialColorOnSurface);
text.setTextColor(tintColor);
}
return view;
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index d4e74d3bb906..081d2a087b07 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -75,8 +75,8 @@ public class AmbientStatusBarView extends ConstraintLayout {
private ShadowInfo mAmbientShadowInfo;
private int mDrawableSize;
private int mDrawableInsetSize;
- private static final float KEY_SHADOW_ALPHA = 0.35f;
- private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
+ private static final float KEY_SHADOW_ALPHA = 0.8f;
+ private static final float AMBIENT_SHADOW_ALPHA = 0.6f;
public AmbientStatusBarView(Context context) {
this(context, null);
@@ -162,11 +162,12 @@ public class AmbientStatusBarView extends ConstraintLayout {
void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
View icon = mStatusIcons.get(iconType);
- if (icon == null) {
- return;
- }
+ if (icon == null) return;
+
if (show && contentDescription != null) {
icon.setContentDescription(contentDescription);
+ icon.setFocusable(true);
+ icon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
icon.setVisibility(show ? View.VISIBLE : View.GONE);
mSystemStatusViewGroup.setVisibility(areAnyStatusIconsVisible() ? View.VISIBLE : View.GONE);
@@ -174,9 +175,12 @@ public class AmbientStatusBarView extends ConstraintLayout {
void setExtraStatusBarItemViews(List<View> views) {
removeAllExtraStatusBarItemViews();
- views.forEach(view -> mExtraSystemStatusViewGroup.addView(view));
+ views.forEach(view -> {
+ view.setFocusable(true);
+ view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ mExtraSystemStatusViewGroup.addView(view);
+ });
}
-
private View fetchStatusIconForResId(int resId) {
final View statusIcon = findViewById(resId);
return Objects.requireNonNull(statusIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index cbdb8827e39c..5cf4b4faed78 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -242,6 +242,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
op.getUid(),
op.getPackageName(),
/* attributionTag= */ attributedOpEntry.getKey(),
+ Context.DEVICE_ID_DEFAULT,
/* active= */ true,
// AppOpsManager doesn't have a way to fetch attribution flags or
// chain ID given an op entry, so default them to none.
@@ -440,14 +441,14 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
* Required to override, delegate to other. Should not be called.
*/
public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
- onOpActiveChanged(op, uid, packageName, null, active,
+ onOpActiveChanged(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT, active,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
// Get active app ops, and check if their attributions are trusted
@Override
public void onOpActiveChanged(String op, int uid, String packageName, String attributionTag,
- boolean active, int attributionFlags, int attributionChainId) {
+ int virtualDeviceId, boolean active, int attributionFlags, int attributionChainId) {
int code = AppOpsManager.strOpToOp(op);
if (DEBUG) {
Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s,%d,%d)", code, uid, packageName,
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 1176cb0523c1..c17055740166 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -52,6 +52,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.app.animation.Interpolators;
import com.android.systemui.DualToneHandler;
+import com.android.systemui.FontStyles;
import com.android.systemui.battery.unified.BatteryColors;
import com.android.systemui.battery.unified.BatteryDrawableState;
import com.android.systemui.battery.unified.BatteryLayersDrawable;
@@ -387,7 +388,8 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null);
mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight);
if (gsfQuickSettings()) {
- mBatteryPercentView.setTypeface(Typeface.create("gsf-label-large", Typeface.NORMAL));
+ mBatteryPercentView.setTypeface(
+ Typeface.create(FontStyles.GSF_LABEL_LARGE, Typeface.NORMAL));
}
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
addView(mBatteryPercentView, new LayoutParams(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index f6b6655dca4d..b6537118324e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -27,7 +27,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlertDialog;
-import android.app.KeyguardManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -320,16 +319,6 @@ public class AuthContainerView extends LinearLayout
mBiometricCallback = new BiometricCallback();
mMSDLPlayer = msdlPlayer;
- // Listener for when device locks from adaptive auth, dismiss prompt
- getContext().getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
- getContext().getMainExecutor(),
- isKeyguardLocked -> {
- if (isKeyguardLocked) {
- onStartedGoingToSleep();
- }
- }
- );
-
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 4faf6ff9f596..d0cb507789a1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -27,6 +27,7 @@ import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -713,12 +714,12 @@ public class AuthController implements
onDialogDismissed(reason);
}
@Inject
- public AuthController(Context context,
+ public AuthController(@Main Context context,
@Application CoroutineScope applicationCoroutineScope,
Execution execution,
CommandQueue commandQueue,
ActivityTaskManager activityTaskManager,
- @NonNull WindowManager windowManager,
+ @NonNull @Main WindowManager windowManager,
@Nullable FingerprintManager fingerprintManager,
@Nullable FaceManager faceManager,
Optional<AuthContextPlugins> contextPlugins,
@@ -737,6 +738,7 @@ public class AuthController implements
@Background DelayableExecutor bgExecutor,
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
+ @NonNull KeyguardManager keyguardManager,
Lazy<ViewCapture> daggerLazyViewCapture,
@NonNull MSDLPlayer msdlPlayer) {
mContext = context;
@@ -768,6 +770,15 @@ public class AuthController implements
mPromptViewModelProvider = promptViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
+ keyguardManager.addKeyguardLockedStateListener(
+ context.getMainExecutor(),
+ isKeyguardLocked -> {
+ if (isKeyguardLocked) {
+ closeDialog("Device lock");
+ }
+ }
+ );
+
mOrientationListener = new BiometricDisplayListener(
context,
mDisplayManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index f6cc72431db0..22d2aaf2a8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -36,6 +36,7 @@ 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
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -69,9 +70,9 @@ import javax.inject.Provider
class AuthRippleController
@Inject
constructor(
- private val sysuiContext: Context,
+ @Main private val sysuiContext: Context,
private val authController: AuthController,
- private val configurationController: ConfigurationController,
+ @Main private val configurationController: ConfigurationController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val keyguardStateController: KeyguardStateController,
private val wakefulnessLifecycle: WakefulnessLifecycle,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java
index 027f6744d4d7..8376850c1a28 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.hardware.biometrics.BiometricSourceType;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -43,7 +44,7 @@ public class BiometricNotificationBroadcastReceiver extends BroadcastReceiver {
private final BiometricNotificationDialogFactory mNotificationDialogFactory;
@Inject
BiometricNotificationBroadcastReceiver(
- Context context,
+ @Main Context context,
BiometricNotificationDialogFactory notificationDialogFactory) {
mContext = context;
mNotificationDialogFactory = notificationDialogFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
index 3b49ce2f10f7..e5c22677dbcc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
@@ -45,6 +45,7 @@ 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.Main;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -145,7 +146,7 @@ public class BiometricNotificationService implements CoreStartable {
};
@Inject
- public BiometricNotificationService(@NonNull Context context,
+ public BiometricNotificationService(@NonNull @Main Context context,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull KeyguardStateController keyguardStateController,
@NonNull Handler handler, @NonNull NotificationManager notificationManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index 178e1112fa6f..d9ed9ca1f07b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -24,7 +24,7 @@ import com.android.systemui.biometrics.shared.model.SensorLocation
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -43,7 +43,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
repository: FingerprintPropertyRepository,
- @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+ @Main configurationInteractor: ConfigurationInteractor,
displayStateInteractor: DisplayStateInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index c3dc2d406cbc..52e85576e756 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -26,6 +26,7 @@ import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.isDefaultOrientation
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -48,9 +49,9 @@ import kotlinx.coroutines.flow.onEach
class SideFpsSensorInteractor
@Inject
constructor(
- private val context: Context,
+ @Main private val context: Context,
fingerprintPropertyRepository: FingerprintPropertyRepository,
- windowManager: WindowManager,
+ @Main windowManager: WindowManager,
displayStateInteractor: DisplayStateInteractor,
fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>,
biometricSettingsRepository: BiometricSettingsRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index 976329580c60..8a5e011cd3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -39,7 +40,6 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlin.math.max
/** Encapsulates business logic for interacting with the UDFPS overlay. */
@SysUISingleton
@@ -55,10 +55,7 @@ constructor(
private fun calculateIconSize(): Int {
val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch)
if (pixelPitch <= 0) {
- Log.e(
- "UdfpsOverlayInteractor",
- "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.",
- )
+ Log.e(TAG, "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.")
}
return (context.resources.getFloat(R.dimen.udfps_icon_size) / pixelPitch).toInt()
}
@@ -84,13 +81,15 @@ constructor(
}
/** Sets whether Udfps overlay should handle touches */
- fun setHandleTouches(shouldHandle: Boolean = true) {
- if (authController.isUdfpsSupported && shouldHandle != _shouldHandleTouches.value) {
+ fun setHandleTouches(shouldHandle: Boolean) {
+ if (authController.isUdfpsSupported) {
fingerprintManager?.setIgnoreDisplayTouches(
requestId.value,
authController.udfpsProps!!.get(0).sensorId,
!shouldHandle,
)
+ } else {
+ Log.d(TAG, "setIgnoreDisplayTouches not set, UDFPS not supported")
}
_shouldHandleTouches.value = shouldHandle
}
@@ -123,12 +122,14 @@ constructor(
// Padding between the fingerprint icon and its bounding box in pixels.
val iconPadding: Flow<Int> =
- udfpsOverlayParams.map { params ->
- val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
- val nativePadding = (sensorWidth - iconSize) / 2
- // padding can be negative when udfpsOverlayParams has not been initialized yet.
- max(0, (nativePadding * params.scaleFactor).toInt())
- }.distinctUntilChanged()
+ udfpsOverlayParams
+ .map { params ->
+ val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
+ val nativePadding = (sensorWidth - iconSize) / 2
+ // padding can be negative when udfpsOverlayParams has not been initialized yet.
+ max(0, (nativePadding * params.scaleFactor).toInt())
+ }
+ .distinctUntilChanged()
companion object {
private const val TAG = "UdfpsOverlayInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index 0ad83ec0c3f5..38b1d7d81332 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -37,7 +37,6 @@ import android.widget.Button
import android.widget.LinearLayout
import android.widget.Space
import android.widget.TextView
-import com.android.settingslib.Utils
import com.android.systemui.biometrics.Utils.ellipsize
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -294,7 +293,7 @@ private fun getListItemBulletGapWidth(resources: Resources): Int =
resources.getDimensionPixelSize(R.dimen.biometric_prompt_content_list_item_bullet_gap_width)
private fun getListItemBulletColor(context: Context): Int =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.materialColorOnSurface)
+ context.getColor(com.android.internal.R.color.materialColorOnSurface)
private fun <T : View> T.width(function: (Int) -> Unit) {
if (width == 0)
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 df34952f4f8d..4dcf26808a9e 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
@@ -982,8 +982,9 @@ private fun Context.getUserBadgedLogoInfo(
activityTaskManager: ActivityTaskManager,
): Pair<Drawable?, String> {
// If the app sets customized icon/description, use the passed-in value directly
- var icon: Drawable? =
- if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null
+ val customizedIcon: Drawable? =
+ prompt.logoBitmap?.let { BitmapDrawable(resources, prompt.logoBitmap) }
+ var icon = customizedIcon
var label = prompt.logoDescription ?: ""
if (icon != null && label.isNotEmpty()) {
return Pair(icon, label)
@@ -1009,12 +1010,11 @@ private fun Context.getUserBadgedLogoInfo(
}
}
- // Add user badge
+ // Add user badge for non-customized logo icon
val userHandle = UserHandle.of(prompt.userInfo.userId)
- if (label.isNotEmpty()) {
- label = packageManager.getUserBadgedLabel(label, userHandle).toString()
+ if (icon != null && icon != customizedIcon) {
+ icon = packageManager.getUserBadgedIcon(icon, userHandle)
}
- icon = icon?.let { packageManager.getUserBadgedIcon(it, userHandle) }
return Pair(icon, label)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index c2a4ee36dec6..555716191c96 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -164,21 +164,12 @@ constructor(
showIndicatorForDeviceEntry: Boolean ->
val callbacks = mutableListOf<LottieCallback>()
if (showIndicatorForDeviceEntry) {
- val indicatorColor =
- com.android.settingslib.Utils.getColorAttrDefaultColor(
- applicationContext,
- com.android.internal.R.attr.materialColorPrimaryFixed
- )
- val outerRimColor =
- com.android.settingslib.Utils.getColorAttrDefaultColor(
- applicationContext,
- com.android.internal.R.attr.materialColorPrimaryFixedDim
- )
- val chevronFill =
- com.android.settingslib.Utils.getColorAttrDefaultColor(
- applicationContext,
- com.android.internal.R.attr.materialColorOnPrimaryFixed
- )
+ val indicatorColor = applicationContext.getColor(
+ com.android.internal.R.color.materialColorPrimaryFixed)
+ val outerRimColor = applicationContext.getColor(
+ com.android.internal.R.color.materialColorPrimaryFixedDim)
+ val chevronFill = applicationContext.getColor(
+ com.android.internal.R.color.materialColorOnPrimaryFixed)
callbacks.add(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
callbacks.add(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
callbacks.add(LottieCallback(KeyPath(".black", "**"), chevronFill))
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index d7a0fc9770ee..9cfb5be478ed 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -405,13 +405,10 @@ internal constructor(
}
// updating icon colors
- val tintColor =
- com.android.settingslib.Utils.getColorAttr(
- context,
- if (item.isActive) InternalR.attr.materialColorOnPrimaryContainer
- else InternalR.attr.materialColorOnSurface,
- )
- .defaultColor
+ val tintColor = context.getColor(
+ if (item.isActive) InternalR.color.materialColorOnPrimaryContainer
+ else InternalR.color.materialColorOnSurface
+ )
// update icons
iconView.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
index a42ae03b2c4e..cdd1b3c3cf54 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
@@ -18,7 +18,6 @@ package com.android.systemui.bouncer.data.repository
import android.content.res.Resources
import com.android.internal.R
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -37,7 +36,7 @@ class EmergencyServicesRepository
constructor(
@Application private val applicationScope: CoroutineScope,
@Main private val resources: Resources,
- @GlobalConfig configurationRepository: ConfigurationRepository,
+ @Main configurationRepository: ConfigurationRepository,
) {
/**
* Whether to enable emergency services calls while the SIM card is locked. This is disabled in
@@ -49,7 +48,7 @@ constructor(
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = getEnableEmergencyCallWhileSimLocked()
+ initialValue = getEnableEmergencyCallWhileSimLocked(),
)
private fun getEnableEmergencyCallWhileSimLocked(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt
index 9f1781177f7a..a286d16826cd 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt
@@ -33,13 +33,15 @@ object KeyguardBouncerConstants {
const val DEFAULT_PIN_LENGTH = 6
object ColorId {
- const val TITLE = com.android.internal.R.attr.materialColorOnSurface
- const val PIN_SHAPES = com.android.internal.R.attr.materialColorOnSurfaceVariant
- const val NUM_PAD_BACKGROUND = com.android.internal.R.attr.materialColorSurfaceContainerHigh
- const val NUM_PAD_BACKGROUND_PRESSED = com.android.internal.R.attr.materialColorPrimaryFixed
- const val NUM_PAD_PRESSED = com.android.internal.R.attr.materialColorOnPrimaryFixed
- const val NUM_PAD_KEY = com.android.internal.R.attr.materialColorOnSurface
- const val NUM_PAD_BUTTON = com.android.internal.R.attr.materialColorOnSecondaryFixed
- const val EMERGENCY_BUTTON = com.android.internal.R.attr.materialColorTertiaryFixed
+ const val TITLE = com.android.internal.R.color.materialColorOnSurface
+ const val PIN_SHAPES = com.android.internal.R.color.materialColorOnSurfaceVariant
+ const val NUM_PAD_BACKGROUND =
+ com.android.internal.R.color.materialColorSurfaceContainerHigh
+ const val NUM_PAD_BACKGROUND_PRESSED =
+ com.android.internal.R.color.materialColorPrimaryFixed
+ const val NUM_PAD_PRESSED = com.android.internal.R.color.materialColorOnPrimaryFixed
+ const val NUM_PAD_KEY = com.android.internal.R.color.materialColorOnSurface
+ const val NUM_PAD_BUTTON = com.android.internal.R.color.materialColorOnSecondaryFixed
+ const val EMERGENCY_BUTTON = com.android.internal.R.color.materialColorTertiaryFixed
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index b2d02edf3c45..a31e61f67e47 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -107,6 +107,7 @@ constructor(
activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
activityOptions.rotationAnimationHint =
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ intent.collectExtraIntentKeys()
try {
activityTaskManager.startActivityAsUser(
null,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
index 074b64e0fab0..69f4f6d30f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -74,3 +74,12 @@ constructor(
/** Returns `true` if the tap gesture should be rejected */
fun isFalseTap(@Penalty penalty: Int): Boolean = manager.isFalseTap(penalty)
}
+
+inline fun FalsingInteractor.runIfNotFalseTap(
+ penalty: Int = FalsingManager.LOW_PENALTY,
+ action: () -> Unit,
+) {
+ if (!isFalseTap(penalty)) {
+ action()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Color.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Color.kt
index d235c95eb906..d53a737480a3 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Color.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Color.kt
@@ -18,6 +18,7 @@ package com.android.systemui.common.shared.model
import android.annotation.AttrRes
import android.annotation.ColorInt
+import android.annotation.ColorRes
/**
* Models a color that can be either a specific [Color.Loaded] value or a resolvable theme
@@ -28,4 +29,6 @@ sealed interface Color {
data class Loaded(@ColorInt val color: Int) : Color
data class Attribute(@AttrRes val attribute: Int) : Color
+
+ data class Resource(@ColorRes val colorRes: Int) : Color
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
index 0869351e727b..6a6c3eb05399 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
@@ -16,10 +16,10 @@
package com.android.systemui.common.shared.model
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
/** Models an icon with a specific tint. */
data class TintedIcon(
val icon: Icon,
- @AttrRes val tint: Int?,
+ @ColorRes val tint: Int?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt
index 7f50e4a06022..ad504d502847 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt
@@ -21,21 +21,11 @@ import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Binds
import dagger.Module
import dagger.Provides
-import javax.inject.Qualifier
-
-/**
- * Annotates elements that provide information from the global configuration.
- *
- * The global configuration is the one associated with the main display. Secondary displays will
- * apply override to the global configuration. Elements annotated with this shouldn't be used for
- * secondary displays.
- */
-@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
@Module
interface ConfigurationModule {
@@ -45,32 +35,32 @@ interface ConfigurationModule {
* now, without annotation the global config associated state is provided.
*/
@Binds
- @Deprecated("Use the @GlobalConfig annotated one instead of this.")
+ @Deprecated("Use the @Main annotated one instead of this.")
fun provideGlobalConfigurationState(
- @GlobalConfig configurationState: ConfigurationState
+ @Main configurationState: ConfigurationState
): ConfigurationState
@Binds
- @Deprecated("Use the @GlobalConfig annotated one instead of this.")
+ @Deprecated("Use the @Main annotated one instead of this.")
fun provideDefaultConfigurationState(
- @GlobalConfig configurationState: ConfigurationInteractor
+ @Main configurationState: ConfigurationInteractor
): ConfigurationInteractor
companion object {
@SysUISingleton
@Provides
- @GlobalConfig
+ @Main
fun provideGlobalConfigurationState(
configStateFactory: ConfigurationStateImpl.Factory,
configurationController: ConfigurationController,
- @Application context: Context,
+ @Main context: Context,
): ConfigurationState {
return configStateFactory.create(context, configurationController)
}
@SysUISingleton
@Provides
- @GlobalConfig
+ @Main
fun provideGlobalConfigurationInteractor(
configurationRepository: ConfigurationRepository
): ConfigurationInteractor {
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index df891523798f..747a2a9bd887 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -23,9 +23,9 @@ import android.view.DisplayInfo
import androidx.annotation.DimenRes
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.wrapper.DisplayUtilsWrapper
import dagger.Binds
@@ -53,8 +53,12 @@ interface ConfigurationRepository {
val onConfigurationChange: Flow<Unit>
val scaleForResolution: Flow<Float>
+
val configurationValues: Flow<Configuration>
+ /** Emits the latest display this configuration controller has been moved to. */
+ val onMovedToDisplay: Flow<Int>
+
fun getResolutionScale(): Float
/** Convenience to context.resources.getDimensionPixelSize() */
@@ -117,6 +121,20 @@ constructor(
configurationController.addCallback(callback)
awaitClose { configurationController.removeCallback(callback) }
}
+ override val onMovedToDisplay: Flow<Int>
+ get() = conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onMovedToDisplay(
+ newDisplayId: Int,
+ newConfiguration: Configuration?,
+ ) {
+ trySend(newDisplayId)
+ }
+ }
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
override val scaleForResolution: StateFlow<Float> =
onConfigurationChange
@@ -162,19 +180,19 @@ abstract class ConfigurationRepositoryModule {
* injected.
*/
@Binds
- @Deprecated("Use the ConfigurationRepository annotated with @GlobalConfig instead.")
+ @Deprecated("Use the ConfigurationRepository annotated with @Main instead.")
@SysUISingleton
abstract fun provideDefaultConfigRepository(
- @GlobalConfig configurationRepository: ConfigurationRepository
+ @Main configurationRepository: ConfigurationRepository
): ConfigurationRepository
companion object {
@Provides
- @GlobalConfig
+ @Main
@SysUISingleton
fun provideGlobalConfigRepository(
context: Context,
- @GlobalConfig configurationController: ConfigurationController,
+ @Main configurationController: ConfigurationController,
factory: ConfigurationRepositoryImpl.Factory,
): ConfigurationRepository {
return factory.create(context, configurationController)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index d648b9c6442b..34679b08cf20 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -23,7 +23,6 @@ import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -184,7 +183,12 @@ constructor(
this@CommunalSceneStartable.isDreaming = isDreaming
if (scene.isCommunal() && isDreaming && timeoutJob == null) {
// If dreaming starts after timeout has expired, ex. if dream restarts under
- // the hub, just close the hub immediately.
+ // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub. The
+ // delay is necessary so the KeyguardInteractor.isAbleToDream flow passes
+ // through that same amount of delay and publishes a new value which is then
+ // picked up by the HomeSceneFamilyResolver such that the next call to
+ // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
+ delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
communalSceneInteractor.changeScene(
CommunalScenes.Blank,
"dream started after timeout",
@@ -218,7 +222,8 @@ constructor(
newScene = CommunalScenes.Blank,
loggingReason = "hub timeout",
transitionKey =
- if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade
+ if (communalSettingsInteractor.isV2FlagEnabled())
+ CommunalTransitionKeys.SimpleFade
else null,
)
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index f9b30c6c2ba1..ea428698e476 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -27,6 +27,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
@@ -535,7 +536,9 @@ constructor(
// Order by creation time descending.
ongoingContent.sortByDescending { it.createdTimestampMillis }
// Resize the items.
- ongoingContent.resizeItems()
+ if (!communalResponsiveGrid()) {
+ ongoingContent.resizeItems()
+ }
// Return the sorted and resized items.
ongoingContent
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 0cdbad40b2d1..862b05bc9b5d 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
@@ -63,24 +63,41 @@ constructor(
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
columnPrefix = "disabledReason",
- initialValue = CommunalEnabledState()
+ initialValue = CommunalEnabledState(),
)
.map { model -> model.enabled }
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
/**
- * Returns true if both the communal trunk-stable flag and resource flag are enabled.
+ * Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
- * The trunk-stable flag is controlled by server rollout and is on all devices. The resource
- * flag is enabled via resource overlay only on products we want the hub to be present on.
+ * This should be used for preventing basic glanceable hub functionality from running on devices
+ * that don't need it.
*
* If this is false, then the hub is definitely not available on the device. If this is true,
* refer to [isCommunalEnabled] which takes into account other factors that can change at
* runtime.
+ *
+ * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android
+ * config boolean. Otherwise, checks the old config_communalServiceEnabled config and
+ * communal_hub flag.
*/
fun isCommunalFlagEnabled(): Boolean = repository.getFlagEnabled()
+ /**
+ * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag
+ * are enabled.
+ *
+ * This should be used to flag off new glanceable hub or dream behavior that should launch
+ * together with the new hub experience that brings the hub to mobile.
+ *
+ * The trunk-stable flag is controlled by server rollout and is on all devices. The Android
+ * config flag is enabled via resource overlay only on products we want the hub to be present
+ * on.
+ */
+ fun isV2FlagEnabled(): Boolean = repository.getV2FlagEnabled()
+
/** The type of background to use for the hub. Used to experiment with different backgrounds */
val communalBackground: Flow<CommunalBackgroundType> =
userInteractor.selectedUserInfo
@@ -120,6 +137,6 @@ constructor(
.stateIn(
scope = bgScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = null
+ initialValue = null,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index da613f58dce8..c0456d5bb46e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -110,6 +110,11 @@ sealed interface CommunalContentModel {
get() = fixedHalfOrResponsiveSize()
}
+ /** An empty spacer to reserve space in the grid. */
+ data class Spacer(override val size: CommunalContentSize) : CommunalContentModel {
+ override val key: String = KEY.spacer()
+ }
+
/** A CTA tile in the glanceable hub view mode which can be dismissed. */
class CtaTileInViewMode : CommunalContentModel {
override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
@@ -171,6 +176,10 @@ sealed interface CommunalContentModel {
fun umo(): String {
return "umo"
}
+
+ fun spacer(): String {
+ return "spacer_${UUID.randomUUID()}"
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
deleted file mode 100644
index 71bfe0c057e4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
+++ /dev/null
@@ -1,110 +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.communal.ui.binder
-
-import android.content.Context
-import android.os.Bundle
-import android.util.SizeF
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.compose.ui.unit.IntSize
-import androidx.core.view.doOnLayout
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalWidgetResizing
-import com.android.systemui.common.ui.view.onLayoutChanged
-import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.util.WidgetViewFactory
-import com.android.systemui.util.kotlin.DisposableHandles
-import com.android.systemui.util.kotlin.toDp
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-
-object CommunalAppWidgetHostViewBinder {
- private const val TAG = "CommunalAppWidgetHostViewBinder"
-
- fun bind(
- context: Context,
- applicationScope: CoroutineScope,
- mainContext: CoroutineContext,
- backgroundContext: CoroutineContext,
- container: FrameLayout,
- model: CommunalContentModel.WidgetContent.Widget,
- size: SizeF?,
- factory: WidgetViewFactory,
- ): DisposableHandle {
- val disposables = DisposableHandles()
-
- val loadingJob =
- applicationScope.launch("$TAG#createWidgetView") {
- val widget = factory.createWidget(context, model, size)
- waitForLayout(container)
- container.post { container.setView(widget) }
- if (communalWidgetResizing()) {
- // Update the app widget size in the background.
- launch("$TAG#updateSize", backgroundContext) {
- container.sizeFlow().flowOn(mainContext).distinctUntilChanged().collect {
- (width, height) ->
- widget.updateAppWidgetSize(
- /* newOptions = */ Bundle(),
- /* minWidth = */ width,
- /* minHeight = */ height,
- /* maxWidth = */ width,
- /* maxHeight = */ height,
- /* ignorePadding = */ true,
- )
- }
- }
- }
- }
-
- disposables += DisposableHandle { loadingJob.cancel() }
- disposables += DisposableHandle { container.removeAllViews() }
-
- return disposables
- }
-
- private suspend fun waitForLayout(container: FrameLayout) = suspendCoroutine { cont ->
- container.doOnLayout { cont.resume(Unit) }
- }
-}
-
-private fun ViewGroup.setView(view: View) {
- if (view.parent == this) {
- return
- }
- (view.parent as? ViewGroup)?.removeView(view)
- addView(view)
-}
-
-private fun View.sizeAsDp(): IntSize = IntSize(width.toDp(context), height.toDp(context))
-
-private fun View.sizeFlow(): Flow<IntSize> = conflatedCallbackFlow {
- if (isLaidOut && !isLayoutRequested) {
- trySend(sizeAsDp())
- }
- val disposable = onLayoutChanged { trySend(sizeAsDp()) }
- awaitClose { disposable.dispose() }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
index 2e12bad744f0..9f19562bb668 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
@@ -16,98 +16,132 @@
package com.android.systemui.communal.ui.view.layout.sections
+import android.os.Bundle
import android.util.SizeF
+import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-import android.widget.FrameLayout
+import android.view.accessibility.AccessibilityNodeInfo
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.Flags.communalWidgetResizing
+import com.android.systemui.Flags.communalHubUseThreadPoolForWidgets
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.ui.binder.CommunalAppWidgetHostViewBinder
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.util.WidgetViewFactory
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.communal.ui.viewmodel.CommunalAppWidgetViewModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
+import java.util.concurrent.Executor
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
class CommunalAppWidgetSection
@Inject
constructor(
- @Application private val applicationScope: CoroutineScope,
- @Main private val mainContext: CoroutineContext,
- @UiBackground private val backgroundContext: CoroutineContext,
- private val factory: WidgetViewFactory,
+ @UiBackground private val uiBgExecutor: Executor,
+ private val interactionHandler: WidgetInteractionHandler,
+ private val viewModelFactory: CommunalAppWidgetViewModel.Factory,
) {
private companion object {
- val DISPOSABLE_TAG = R.id.communal_widget_disposable_tag
+ const val TAG = "CommunalAppWidgetSection"
+ val LISTENER_TAG = R.id.communal_widget_listener_tag
+
+ val poolSize by lazy { Runtime.getRuntime().availableProcessors().coerceAtLeast(2) }
+
+ /**
+ * This executor is used for widget inflation. Parameters match what launcher uses. See
+ * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR].
+ */
+ val widgetExecutor by lazy {
+ ThreadPoolExecutor(
+ /*corePoolSize*/ poolSize,
+ /*maxPoolSize*/ poolSize,
+ /*keepAlive*/ 1,
+ /*unit*/ TimeUnit.SECONDS,
+ /*workQueue*/ LinkedBlockingQueue(),
+ )
+ }
}
@Composable
fun Widget(
- viewModel: BaseCommunalViewModel,
+ isFocusable: Boolean,
+ openWidgetEditor: () -> Unit,
model: CommunalContentModel.WidgetContent.Widget,
size: SizeF,
modifier: Modifier = Modifier,
) {
- val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+ val viewModel = rememberViewModel("$TAG#viewModel") { viewModelFactory.create() }
+ val longClickLabel = stringResource(R.string.accessibility_action_label_edit_widgets)
+ val accessibilityDelegate =
+ remember(longClickLabel, openWidgetEditor) {
+ WidgetAccessibilityDelegate(longClickLabel, openWidgetEditor)
+ }
AndroidView(
factory = { context ->
- FrameLayout(context).apply {
- layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- )
-
- // Need to attach the disposable handle to the view here instead of storing
- // the state in the composable in order to properly support lazy lists. In a
- // lazy list, when the composable is no longer in view - it will exit
- // composition and any state inside the composable will be lost. However,
- // the View instance will be re-used. Therefore we can store data on the view
- // in order to preserve it.
- setTag(
- DISPOSABLE_TAG,
- CommunalAppWidgetHostViewBinder.bind(
- context = context,
- container = this,
- model = model,
- size = if (!communalWidgetResizing()) size else null,
- factory = factory,
- applicationScope = applicationScope,
- mainContext = mainContext,
- backgroundContext = backgroundContext,
- ),
- )
-
- accessibilityDelegate = viewModel.widgetAccessibilityDelegate
+ CommunalAppWidgetHostView(context, interactionHandler).apply {
+ if (communalHubUseThreadPoolForWidgets()) {
+ setExecutor(widgetExecutor)
+ } else {
+ setExecutor(uiBgExecutor)
+ }
}
},
- update = { container ->
- container.importantForAccessibility =
+ update = { view ->
+ view.accessibilityDelegate = accessibilityDelegate
+ view.importantForAccessibility =
if (isFocusable) {
IMPORTANT_FOR_ACCESSIBILITY_AUTO
} else {
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
}
- },
- onRelease = { view ->
- val disposable = (view.getTag(DISPOSABLE_TAG) as? DisposableHandle)
- disposable?.dispose()
+ view.setAppWidget(model.appWidgetId, model.providerInfo)
+ // To avoid calling the expensive setListener method on every recomposition if
+ // the appWidgetId hasn't changed, we store the current appWidgetId of the view in
+ // a tag.
+ if ((view.getTag(LISTENER_TAG) as? Int) != model.appWidgetId) {
+ viewModel.setListener(model.appWidgetId, view)
+ view.setTag(LISTENER_TAG, model.appWidgetId)
+ }
+ viewModel.updateSize(size, view)
},
modifier = modifier,
// For reusing composition in lazy lists.
onReset = {},
)
}
+
+ private class WidgetAccessibilityDelegate(
+ private val longClickLabel: String,
+ private val longClickAction: () -> Unit,
+ ) : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ // Hint user to long press in order to enter edit mode
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ longClickLabel.lowercase(),
+ )
+ )
+ }
+
+ override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
+ when (action) {
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id -> {
+ longClickAction()
+ return true
+ }
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index a339af3694e7..099a85926020 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -19,7 +19,6 @@ package com.android.systemui.communal.ui.viewmodel
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.os.UserHandle
-import android.view.View
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
@@ -80,9 +79,6 @@ abstract class BaseCommunalViewModel(
*/
val glanceableTouchAvailable: Flow<Boolean> = anyOf(not(isTouchConsumed), isNestedScrolling)
- /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
- open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null
-
/**
* The up-to-date value of the grid scroll offset. persisted to interactor on
* {@link #persistScrollPosition}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
new file mode 100644
index 000000000000..6bafd14f9359
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetHostView
+import android.os.Bundle
+import android.util.SizeF
+import com.android.app.tracing.coroutines.coroutineScopeTraced
+import com.android.app.tracing.coroutines.withContextTraced
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import dagger.Lazy
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.receiveAsFlow
+
+/** View model for showing a widget. */
+class CommunalAppWidgetViewModel
+@AssistedInject
+constructor(
+ @UiBackground private val backgroundContext: CoroutineContext,
+ private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+ private val listenerDelegateFactory: AppWidgetHostListenerDelegate.Factory,
+ private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+ private val multiUserHelper: GlanceableHubMultiUserHelper,
+) : ExclusiveActivatable() {
+
+ private companion object {
+ const val TAG = "CommunalAppWidgetViewModel"
+ const val CHANNEL_CAPACITY = 10
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): CommunalAppWidgetViewModel
+ }
+
+ private val requests =
+ Channel<Request>(capacity = CHANNEL_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ fun setListener(appWidgetId: Int, listener: AppWidgetHostListener) {
+ requests.trySend(SetListener(appWidgetId, listener))
+ }
+
+ fun updateSize(size: SizeF, view: AppWidgetHostView) {
+ requests.trySend(UpdateSize(size, view))
+ }
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScopeTraced("$TAG#onActivated") {
+ requests.receiveAsFlow().collect { request ->
+ when (request) {
+ is SetListener -> handleSetListener(request.appWidgetId, request.listener)
+ is UpdateSize -> handleUpdateSize(request.size, request.view)
+ }
+ }
+ }
+
+ awaitCancellation()
+ }
+
+ private suspend fun handleSetListener(appWidgetId: Int, listener: AppWidgetHostListener) =
+ withContextTraced("$TAG#setListenerInner", backgroundContext) {
+ if (
+ multiUserHelper.glanceableHubHsumFlagEnabled &&
+ multiUserHelper.isInHeadlessSystemUser()
+ ) {
+ // If the widget view is created in the headless system user, the widget host lives
+ // remotely in the foreground user, and therefore the host listener needs to be
+ // registered through the widget manager.
+ with(glanceableHubWidgetManagerLazy.get()) {
+ setAppWidgetHostListener(appWidgetId, listenerDelegateFactory.create(listener))
+ }
+ } else {
+ // Instead of setting the view as the listener directly, we wrap the view in a
+ // delegate which ensures the callbacks always get called on the main thread.
+ with(appWidgetHostLazy.get()) {
+ setListener(appWidgetId, listenerDelegateFactory.create(listener))
+ }
+ }
+ }
+
+ private suspend fun handleUpdateSize(size: SizeF, view: AppWidgetHostView) =
+ withContextTraced("$TAG#updateSizeInner", backgroundContext) {
+ view.updateAppWidgetSize(
+ /* newOptions = */ Bundle(),
+ /* minWidth = */ size.width.toInt(),
+ /* minHeight = */ size.height.toInt(),
+ /* maxWidth = */ size.width.toInt(),
+ /* maxHeight = */ size.height.toInt(),
+ /* ignorePadding = */ true,
+ )
+ }
+}
+
+private sealed interface Request
+
+/**
+ * [Request] to call [CommunalAppWidgetHost.setListener] to tie this view to a particular widget.
+ * Since this is involves an IPC to system_server, the call is asynchronous and happens in the
+ * background.
+ */
+private data class SetListener(val appWidgetId: Int, val listener: AppWidgetHostListener) : Request
+
+/**
+ * [Request] to call [AppWidgetHostView.updateAppWidgetSize] to notify the widget provider of the
+ * new size. Since this is involves an IPC to system_server, the call is asynchronous and happens in
+ * the background.
+ */
+private data class UpdateSize(val size: SizeF, val view: AppWidgetHostView) : Request
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
new file mode 100644
index 000000000000..7d5b196dfaa8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import android.annotation.SuppressLint
+import android.app.DreamManager
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.kotlin.isDevicePluggedIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class CommunalToDreamButtonViewModel
+@AssistedInject
+constructor(
+ @Background private val backgroundContext: CoroutineContext,
+ batteryController: BatteryController,
+ private val dreamManager: DreamManager,
+) : ExclusiveActivatable() {
+
+ private val _requests = Channel<Unit>(Channel.BUFFERED)
+
+ /** Whether we should show a button on hub to switch to dream. */
+ @SuppressLint("MissingPermission")
+ val shouldShowDreamButtonOnHub =
+ batteryController
+ .isDevicePluggedIn()
+ .distinctUntilChanged()
+ .map { isPluggedIn -> isPluggedIn && dreamManager.canStartDreaming(true) }
+ .flowOn(backgroundContext)
+
+ /** Handle a tap on the "show dream" button. */
+ fun onShowDreamButtonTap() {
+ _requests.trySend(Unit)
+ }
+
+ @SuppressLint("MissingPermission")
+ override suspend fun onActivated(): Nothing = coroutineScope {
+ launch {
+ _requests.receiveAsFlow().collectLatest {
+ withContext(backgroundContext) { dreamManager.startDream() }
+ }
+ }
+
+ awaitCancellation()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): CommunalToDreamButtonViewModel
+ }
+}
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 9cd6465266d4..ddc4d1c10690 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
@@ -17,11 +17,8 @@
package com.android.systemui.communal.ui.viewmodel
import android.content.ComponentName
-import android.content.res.Resources
-import android.os.Bundle
-import android.view.View
-import android.view.accessibility.AccessibilityNodeInfo
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -44,7 +41,6 @@ import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -84,13 +80,12 @@ constructor(
@Main val mainDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
@Background private val bgScope: CoroutineScope,
- @Main private val resources: Resources,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
keyguardInteractor: KeyguardInteractor,
private val keyguardIndicationController: KeyguardIndicationController,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -218,39 +213,6 @@ constructor(
}
.distinctUntilChanged()
- override val widgetAccessibilityDelegate =
- object : View.AccessibilityDelegate() {
- override fun onInitializeAccessibilityNodeInfo(
- host: View,
- info: AccessibilityNodeInfo,
- ) {
- super.onInitializeAccessibilityNodeInfo(host, info)
- // Hint user to long press in order to enter edit mode
- info.addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
- resources
- .getString(R.string.accessibility_action_label_edit_widgets)
- .lowercase(),
- )
- )
- }
-
- override fun performAccessibilityAction(
- host: View,
- action: Int,
- args: Bundle?,
- ): Boolean {
- when (action) {
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id -> {
- onOpenWidgetEditor()
- return true
- }
- }
- return super.performAccessibilityAction(host, action, args)
- }
- }
-
private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow()
@@ -293,6 +255,10 @@ constructor(
}
override fun onLongClick() {
+ if (Flags.glanceableHubDirectEditMode()) {
+ onOpenWidgetEditor(false)
+ return
+ }
setCurrentPopupType(PopupType.CustomizeWidgetButton)
}
@@ -372,6 +338,9 @@ constructor(
val communalBackground: Flow<CommunalBackgroundType> =
communalSettingsInteractor.communalBackground
+ /** See [CommunalSettingsInteractor.isV2FlagEnabled] */
+ fun v2FlagEnabled(): Boolean = communalSettingsInteractor.isV2FlagEnabled()
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt b/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt
index 421774462974..481acc935f41 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt
@@ -18,7 +18,6 @@ package com.android.systemui.communal.util
import android.content.Context
import android.graphics.Color
-import com.android.settingslib.Utils
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -54,9 +53,6 @@ constructor(
private fun loadBackgroundColor(): Color =
Color.valueOf(
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.materialColorPrimary
- )
+ context.getColor(com.android.internal.R.color.materialColorPrimary)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt
new file mode 100644
index 000000000000..36cc1c051336
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
+
+object ResizeUtils {
+ /**
+ * Resizes ongoing items such that we don't mix regular content with ongoing content.
+ *
+ * NOTE: This is *NOT* a pure function, as it modifies items in the input list.
+ *
+ * Assumptions:
+ * 1. Ongoing content is always at the start of the list.
+ * 2. The maximum size of ongoing content is 2 rows.
+ */
+ fun resizeOngoingItems(
+ list: List<CommunalContentModel>,
+ numRows: Int,
+ ): List<CommunalContentModel> {
+ val finalizedList = mutableListOf<CommunalContentModel>()
+ val numOngoing = list.count { it is CommunalContentModel.Ongoing }
+ // Calculate the number of extra rows we have if each ongoing item were to take up a single
+ // row. This is the number of rows we have to distribute across items.
+ var extraRows =
+ if (numOngoing % numRows == 0) {
+ 0
+ } else {
+ numRows - (numOngoing % numRows)
+ }
+ var remainingRows = numRows
+
+ for (item in list) {
+ if (item is CommunalContentModel.Ongoing) {
+ if (remainingRows == 0) {
+ // Start a new column.
+ remainingRows = numRows
+ }
+ val newSize = if (extraRows > 0 && remainingRows > 1) 2 else 1
+ item.size = CommunalContentSize.Responsive(newSize)
+ finalizedList.add(item)
+ extraRows -= (newSize - 1)
+ remainingRows -= newSize
+ } else {
+ if (numOngoing > 0 && remainingRows > 0) {
+ finalizedList.add(
+ CommunalContentModel.Spacer(CommunalContentSize.Responsive(remainingRows))
+ )
+ }
+ remainingRows = -1
+ finalizedList.add(item)
+ }
+ }
+ return finalizedList
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
deleted file mode 100644
index 50d86a24be96..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ /dev/null
@@ -1,117 +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.communal.util
-
-import android.content.Context
-import android.os.Bundle
-import android.util.SizeF
-import com.android.app.tracing.coroutines.withContextTraced as withContext
-import com.android.systemui.Flags
-import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
-import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
-import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
-import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
-import com.android.systemui.dagger.qualifiers.UiBackground
-import dagger.Lazy
-import java.util.concurrent.Executor
-import java.util.concurrent.LinkedBlockingQueue
-import java.util.concurrent.ThreadPoolExecutor
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-
-/** Factory for creating [CommunalAppWidgetHostView] in a background thread. */
-class WidgetViewFactory
-@Inject
-constructor(
- @UiBackground private val uiBgContext: CoroutineContext,
- @UiBackground private val uiBgExecutor: Executor,
- private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
- private val interactionHandler: WidgetInteractionHandler,
- private val listenerFactory: AppWidgetHostListenerDelegate.Factory,
- private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
- private val multiUserHelper: GlanceableHubMultiUserHelper,
-) {
- suspend fun createWidget(
- context: Context,
- model: CommunalContentModel.WidgetContent.Widget,
- size: SizeF?,
- ): CommunalAppWidgetHostView =
- withContext("$TAG#createWidget", uiBgContext) {
- val view =
- CommunalAppWidgetHostView(context, interactionHandler).apply {
- if (Flags.communalHubUseThreadPoolForWidgets()) {
- setExecutor(widgetExecutor)
- } else {
- setExecutor(uiBgExecutor)
- }
- setAppWidget(model.appWidgetId, model.providerInfo)
- }
-
- if (
- multiUserHelper.glanceableHubHsumFlagEnabled &&
- multiUserHelper.isInHeadlessSystemUser()
- ) {
- // If the widget view is created in the headless system user, the widget host lives
- // remotely in the foreground user, and therefore the host listener needs to be
- // registered through the widget manager.
- with(glanceableHubWidgetManagerLazy.get()) {
- setAppWidgetHostListener(model.appWidgetId, listenerFactory.create(view))
- }
- } else {
- // Instead of setting the view as the listener directly, we wrap the view in a
- // delegate which ensures the callbacks always get called on the main thread.
- with(appWidgetHostLazy.get()) {
- setListener(model.appWidgetId, listenerFactory.create(view))
- }
- }
-
- if (size != null) {
- view.updateAppWidgetSize(
- /* newOptions = */ Bundle(),
- /* minWidth = */ size.width.toInt(),
- /* minHeight = */ size.height.toInt(),
- /* maxWidth = */ size.width.toInt(),
- /* maxHeight = */ size.height.toInt(),
- /* ignorePadding = */ true,
- )
- }
- view
- }
-
- private companion object {
- const val TAG = "WidgetViewFactory"
-
- val poolSize = Runtime.getRuntime().availableProcessors().coerceAtLeast(2)
-
- /**
- * This executor is used for widget inflation. Parameters match what launcher uses. See
- * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR].
- */
- val widgetExecutor =
- ThreadPoolExecutor(
- /*corePoolSize*/ poolSize,
- /*maxPoolSize*/ poolSize,
- /*keepAlive*/ 1,
- /*unit*/ TimeUnit.SECONDS,
- /*workQueue*/ LinkedBlockingQueue(),
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
index f3416216afdd..7d80acd1f439 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
@@ -36,7 +36,7 @@ constructor(
) : AppWidgetHostListener {
@AssistedFactory
- interface Factory {
+ fun interface Factory {
fun create(listener: AppWidgetHostListener): AppWidgetHostListenerDelegate
}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
index 8527dcb3794f..35592a5d87d9 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
@@ -29,6 +29,7 @@ import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dreams.DreamOverlayStateController;
@@ -58,6 +59,14 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
private final LifecycleOwner mLifecycleOwner;
private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+
+ private final Observer<Collection<ComplicationViewModel>> mComplicationViewModelObserver =
+ new Observer<>() {
+ @Override
+ public void onChanged(Collection<ComplicationViewModel> complicationViewModels) {
+ updateComplications(complicationViewModels);
+ }
+ };
@VisibleForTesting
boolean mIsAnimationEnabled;
@@ -80,13 +89,6 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
}
- @Override
- protected void onInit() {
- super.onInit();
- mComplicationCollectionViewModel.getComplications().observe(mLifecycleOwner,
- complicationViewModels -> updateComplications(complicationViewModels));
- }
-
/**
* Returns the region in display space occupied by complications. Touches in this region
* (composed of a collection of individual rectangular regions) should be directed to the
@@ -166,10 +168,14 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
@Override
protected void onViewAttached() {
+ mComplicationCollectionViewModel.getComplications().observe(mLifecycleOwner,
+ mComplicationViewModelObserver);
}
@Override
protected void onViewDetached() {
+ mComplicationCollectionViewModel.getComplications().removeObserver(
+ mComplicationViewModelObserver);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 78a8a42e2743..9ae106c3ab39 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -708,6 +708,14 @@ public class FrameworkServicesModule {
return context.getSystemService(WindowManager.class);
}
+ /** A window manager working for the default display only. */
+ @Provides
+ @Singleton
+ @Main
+ static WindowManager provideMainWindowManager(WindowManager windowManager) {
+ return windowManager;
+ }
+
@Provides
@Singleton
static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index 3072f74dff2b..ddc88a839a63 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -21,6 +21,7 @@ import android.util.DisplayMetrics;
import android.view.Display;
import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.PluginsModule;
import com.android.systemui.unfold.UnfoldTransitionModule;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
@@ -62,6 +63,13 @@ public class GlobalModule {
return context.getApplicationContext();
}
+ /** Provides the default content with the main annotation. */
+ @Provides
+ @Main
+ public Context provideMainContext(Context context) {
+ return context;
+ }
+
/**
* @deprecated Deprecdated because {@link Display#getMetrics} is deprecated.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index e78ce3bbb0d1..f804c2e80ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -24,6 +24,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.util.InitializationChecker;
+import com.android.wm.shell.dagger.WMComponent;
import dagger.BindsInstance;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 1fc549469b55..3050cba12f09 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -61,6 +61,7 @@ import com.android.systemui.scene.SceneContainerFrameworkModule;
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.MultiUserUtilsModule;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.settings.brightness.dagger.BrightnessSliderModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeModule;
import com.android.systemui.startable.Dependencies;
@@ -124,6 +125,7 @@ import javax.inject.Named;
AccessibilityRepositoryModule.class,
AospPolicyModule.class,
BatterySaverModule.class,
+ BrightnessSliderModule.class,
CentralSurfacesModule.class,
ClipboardOverlayOverrideModule.class,
CollapsedStatusBarFragmentStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 580896cb534e..00eead6eb7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -21,7 +21,7 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
-import com.android.systemui.common.ui.GlobalConfig;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.PerUser;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -128,14 +128,14 @@ public interface SysUIComponent {
* Creates a ConfigurationController.
*/
@SysUISingleton
- @GlobalConfig
+ @Main
ConfigurationController getConfigurationController();
/**
* Creates a ConfigurationForwarder.
*/
@SysUISingleton
- @GlobalConfig
+ @Main
ConfigurationForwarder getConfigurationForwarder();
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index ba579188a8c9..673ee6dda259 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -37,6 +37,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -82,6 +83,7 @@ public class DozeScreenState implements DozeMachine.Part {
private final DozeLog mDozeLog;
private final DozeScreenBrightness mDozeScreenBrightness;
private final SelectedUserInteractor mSelectedUserInteractor;
+ private final DozeInteractor mDozeInteractor;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
@@ -97,6 +99,7 @@ public class DozeScreenState implements DozeMachine.Part {
Provider<UdfpsController> udfpsControllerProvider,
DozeLog dozeLog,
DozeScreenBrightness dozeScreenBrightness,
+ DozeInteractor dozeInteractor,
SelectedUserInteractor selectedUserInteractor) {
mDozeService = service;
mHandler = handler;
@@ -108,6 +111,7 @@ public class DozeScreenState implements DozeMachine.Part {
mDozeLog = dozeLog;
mDozeScreenBrightness = dozeScreenBrightness;
mSelectedUserInteractor = selectedUserInteractor;
+ mDozeInteractor = dozeInteractor;
updateUdfpsController();
if (mUdfpsController == null) {
@@ -225,6 +229,7 @@ public class DozeScreenState implements DozeMachine.Part {
if (screenState != Display.STATE_UNKNOWN) {
if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
mDozeService.setDozeScreenState(screenState);
+ mDozeInteractor.setDozeScreenState(screenState);
if (screenState == Display.STATE_DOZE) {
// If we're entering doze, update the doze screen brightness. We might have been
// clamping it to the dim brightness during the screen off animation, and we should
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
index e76fd47c74de..c425bee74b2b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
@@ -25,7 +25,7 @@ import android.os.PatternMatcher
import android.os.RemoteException
import android.service.dreams.IDreamManager
import android.util.Log
-import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.qualifiers.SystemUser
import com.android.systemui.dreams.dagger.DreamModule
import com.android.systemui.log.LogBuffer
@@ -48,6 +48,7 @@ constructor(
@SystemUser monitor: Monitor,
private val packageManager: PackageManager,
private val dreamManager: IDreamManager,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
@DreamLog private val logBuffer: LogBuffer,
) : ConditionalCoreStartable(monitor) {
private var currentRegisteredState = false
@@ -90,7 +91,7 @@ constructor(
}
if (
- Flags.communalHubOnMobile() &&
+ communalSettingsInteractor.isV2FlagEnabled() &&
packageManager.getComponentEnabledSetting(overlayServiceComponent) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
) {
@@ -111,7 +112,7 @@ constructor(
}
// Enable for hub on mobile
- if (Flags.communalHubOnMobile()) {
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
// Not available on TV or auto
if (
packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) ||
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index aee3a457e18a..571b37f43fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -18,7 +18,6 @@ package com.android.systemui.dreams;
import static android.service.dreams.Flags.dreamWakeRedirect;
-import static com.android.systemui.Flags.communalHubOnMobile;
import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_MANAGER;
@@ -60,6 +59,7 @@ import com.android.systemui.ambient.touch.TouchMonitor;
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor;
import com.android.systemui.communal.shared.log.CommunalUiEvent;
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.communal.shared.model.CommunalTransitionKeys;
@@ -171,6 +171,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final SceneInteractor mSceneInteractor;
private final CommunalInteractor mCommunalInteractor;
+ private final CommunalSettingsInteractor mCommunalSettingsInteractor;
private boolean mCommunalAvailable;
@@ -383,6 +384,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
KeyguardUpdateMonitor keyguardUpdateMonitor,
ScrimManager scrimManager,
CommunalInteractor communalInteractor,
+ CommunalSettingsInteractor communalSettingsInteractor,
SceneInteractor sceneInteractor,
SystemDialogsCloser systemDialogsCloser,
UiEventLogger uiEventLogger,
@@ -411,6 +413,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mDreamOverlayCallbackController = dreamOverlayCallbackController;
mWindowTitle = windowTitle;
mCommunalInteractor = communalInteractor;
+ mCommunalSettingsInteractor = communalSettingsInteractor;
mSceneInteractor = sceneInteractor;
mSystemDialogsCloser = systemDialogsCloser;
mGestureInteractor = gestureInteractor;
@@ -488,7 +491,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
final ArrayList<TouchHandler> touchHandlers = new ArrayList<>(
List.of(dreamComplicationComponent.getHideComplicationTouchHandler()));
- if (!communalHubOnMobile()) {
+ if (!mCommunalSettingsInteractor.isV2FlagEnabled()) {
// Do not add the communal touch handler for glanceable hub v2 since there is no dream
// to hub swipe gesture.
touchHandlers.add(dreamOverlayComponent.getCommunalTouchHandler());
@@ -575,7 +578,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
} else {
mCommunalInteractor.changeScene(CommunalScenes.Communal,
"dream wake requested",
- communalHubOnMobile() ? CommunalTransitionKeys.INSTANCE.getSimpleFade() : null);
+ mCommunalSettingsInteractor.isV2FlagEnabled()
+ ? CommunalTransitionKeys.INSTANCE.getSimpleFade() : null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index 7242770e72e5..e2646353bc19 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -21,6 +21,9 @@ import com.android.systemui.CoreStartable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.ALL_APPS
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
+import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.ContextualEducationMetricsLogger
@@ -37,6 +40,7 @@ import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.time.Clock
+import java.time.Instant
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
@@ -48,6 +52,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.merge
@@ -71,6 +76,8 @@ constructor(
const val TAG = "KeyboardTouchpadEduInteractor"
const val MAX_SIGNAL_COUNT: Int = 2
const val MAX_EDUCATION_SHOW_COUNT: Int = 2
+ const val MAX_TOAST_PER_USAGE_SESSION: Int = 2
+
val usageSessionDuration =
getDurationForConfig("persist.contextual_edu.usage_session_sec", 3.days)
val minIntervalBetweenEdu =
@@ -110,6 +117,16 @@ constructor(
awaitClose { overviewProxyService.removeCallback(listener) }
}
+ private val gestureModelMap: Flow<Map<GestureType, GestureEduModel>> =
+ combine(
+ contextualEducationInteractor.backGestureModelFlow,
+ contextualEducationInteractor.homeGestureModelFlow,
+ contextualEducationInteractor.overviewGestureModelFlow,
+ contextualEducationInteractor.allAppsGestureModelFlow,
+ ) { back, home, overview, allApps ->
+ mapOf(BACK to back, HOME to home, OVERVIEW to overview, ALL_APPS to allApps)
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
override fun start() {
backgroundScope.launch {
@@ -211,7 +228,11 @@ constructor(
private suspend fun incrementSignalCount(gestureType: GestureType) {
val targetDevice = getTargetDevice(gestureType)
- if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) {
+ if (
+ isTargetDeviceConnected(targetDevice) &&
+ hasInitialDelayElapsed(targetDevice) &&
+ isMinIntervalForToastEduElapsed(gestureType)
+ ) {
contextualEducationInteractor.incrementSignalCount(gestureType)
}
}
@@ -223,6 +244,28 @@ constructor(
}
}
+ private suspend fun isMinIntervalForToastEduElapsed(gestureType: GestureType): Boolean {
+ val gestureModelMap = gestureModelMap.first()
+ // Only perform checking if the next edu is toast (i.e. no education is shown yet)
+ if (gestureModelMap[gestureType]?.educationShownCount != 0) {
+ return true
+ }
+
+ val wasLastEduToast = { gesture: GestureEduModel -> gesture.educationShownCount == 1 }
+ val toastEduTimesInCurrentSession: List<Instant> =
+ gestureModelMap.values
+ .filter { wasLastEduToast(it) }
+ .mapNotNull { it.lastEducationTime }
+ .filter { it >= clock.instant().minusSeconds(usageSessionDuration.inWholeSeconds) }
+
+ return if (toastEduTimesInCurrentSession.size >= MAX_TOAST_PER_USAGE_SESSION) {
+ val lastToastTime: Instant? = toastEduTimesInCurrentSession.maxOrNull()
+ clock.instant().isAfter(lastToastTime?.plusSeconds(usageSessionDuration.inWholeSeconds))
+ } else {
+ true
+ }
+ }
+
/**
* Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
* be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
index 058e5874ae7c..950a727aedae 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
@@ -79,6 +79,9 @@ private fun buildScreenConfig() =
bodyResId = R.string.tutorial_action_key_guidance,
titleSuccessResId = R.string.tutorial_action_key_success_title,
bodySuccessResId = R.string.tutorial_action_key_success_body,
+ // error state for action key is not implemented yet so below should never appear
+ titleErrorResId = R.string.gesture_error_title,
+ bodyErrorResId = R.string.touchpad_action_key_error_body,
),
animations = TutorialScreenConfig.Animations(educationResId = R.raw.action_key_edu),
)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index 1b044de5cf63..c40adfe6baf8 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -20,6 +20,7 @@ import android.content.res.Configuration
import androidx.annotation.RawRes
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -33,25 +34,44 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
sealed interface TutorialActionState {
data object NotStarted : TutorialActionState
data class InProgress(
- val progress: Float = 0f,
- val startMarker: String? = null,
- val endMarker: String? = null,
- ) : TutorialActionState
+ override val progress: Float = 0f,
+ override val startMarker: String? = null,
+ override val endMarker: String? = null,
+ ) : TutorialActionState, Progress
data class Finished(@RawRes val successAnimation: Int) : TutorialActionState
+
+ data object Error : TutorialActionState
+
+ data class InProgressAfterError(val inProgress: InProgress) :
+ TutorialActionState, Progress by inProgress
+}
+
+interface Progress {
+ val progress: Float
+ val startMarker: String?
+ val endMarker: String?
}
@Composable
@@ -125,17 +145,23 @@ fun TutorialDescription(
config: TutorialScreenConfig,
modifier: Modifier = Modifier,
) {
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
val (titleTextId, bodyTextId) =
- if (actionState is Finished) {
- config.strings.titleSuccessResId to config.strings.bodySuccessResId
- } else {
- config.strings.titleResId to config.strings.bodyResId
+ when (actionState) {
+ is Finished -> config.strings.titleSuccessResId to config.strings.bodySuccessResId
+ Error,
+ is InProgressAfterError ->
+ config.strings.titleErrorResId to config.strings.bodyErrorResId
+ is NotStarted,
+ is InProgress -> config.strings.titleResId to config.strings.bodyResId
}
Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
Text(
text = stringResource(id = titleTextId),
style = MaterialTheme.typography.displayLarge,
color = config.colors.title,
+ modifier = Modifier.focusRequester(focusRequester).focusable(),
)
Spacer(modifier = Modifier.height(16.dp))
Text(
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
index abd39cc8dea8..b0816ce608a0 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
@@ -36,6 +36,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.Ref
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.util.lerp
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.compose.LottieAnimation
@@ -44,9 +47,12 @@ import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.LottieDynamicProperties
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.res.R
@Composable
fun TutorialAnimation(
@@ -68,16 +74,18 @@ fun TutorialAnimation(
},
) { state ->
when (state) {
- NotStarted::class ->
+ NotStarted::class,
+ Error::class ->
EducationAnimation(
config.animations.educationResId,
config.colors.animationColors,
)
- InProgress::class ->
+ InProgress::class,
+ InProgressAfterError::class ->
InProgressAnimation(
// actionState can be already of different class while this composable is
// transitioning to another one
- actionState as? InProgress,
+ actionState as? Progress,
config.animations.educationResId,
config.colors.animationColors,
)
@@ -104,11 +112,15 @@ private fun EducationAnimation(
isPlaying = isPlaying,
restartOnPlay = false,
)
+ val animationDescription = stringResource(R.string.tutorial_animation_content_description)
LottieAnimation(
composition = composition,
progress = { progress },
dynamicProperties = animationProperties,
- modifier = Modifier.fillMaxSize().clickable { isPlaying = !isPlaying },
+ modifier =
+ Modifier.fillMaxSize()
+ .clickable { isPlaying = !isPlaying }
+ .semantics { contentDescription = animationDescription },
)
}
@@ -130,14 +142,14 @@ private fun SuccessAnimation(
@Composable
private fun InProgressAnimation(
- state: InProgress?,
+ state: Progress?,
@RawRes inProgressAnimationId: Int,
animationProperties: LottieDynamicProperties,
) {
// Caching latest progress for when we're animating this view away and state is null.
// Without this there's jumpcut in the animation while it's animating away.
// state should never be null when composable appears, only when disappearing
- val cached = remember { Ref<InProgress>() }
+ val cached = remember { Ref<Progress>() }
cached.value = state ?: cached.value
val progress = cached.value?.progress ?: 0f
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
index 60dfed3a67a4..26259912741a 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
@@ -38,6 +38,8 @@ data class TutorialScreenConfig(
@StringRes val bodyResId: Int,
@StringRes val titleSuccessResId: Int,
@StringRes val bodySuccessResId: Int,
+ @StringRes val titleErrorResId: Int,
+ @StringRes val bodyErrorResId: Int,
)
data class Animations(@RawRes val educationResId: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index 1e9be09bc3f3..246f6571f2da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyboard.backlight.ui.view
-import android.annotation.AttrRes
import android.annotation.ColorInt
import android.app.Dialog
import android.content.Context
@@ -38,7 +37,6 @@ import android.widget.LinearLayout.LayoutParams
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
import androidx.annotation.IdRes
import androidx.core.view.setPadding
-import com.android.settingslib.Utils
import com.android.systemui.res.R
class KeyboardBacklightDialog(
@@ -80,25 +78,25 @@ class KeyboardBacklightDialog(
@ColorInt
private val filledRectangleColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ context.getColor(com.android.internal.R.color.materialColorPrimary)
@ColorInt
private val emptyRectangleColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
+ context.getColor(com.android.internal.R.color.materialColorOutlineVariant)
@ColorInt
private val backgroundColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
+ context.getColor(com.android.internal.R.color.materialColorSurfaceBright)
@ColorInt
private val defaultIconColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ context.getColor(com.android.internal.R.color.materialColorOnPrimary)
@ColorInt
private val defaultIconBackgroundColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ context.getColor(com.android.internal.R.color.materialColorPrimary)
@ColorInt
private val dimmedIconColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ context.getColor(com.android.internal.R.color.materialColorOnSurface)
@ColorInt
private val dimmedIconBackgroundColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
+ context.getColor(com.android.internal.R.color.materialColorSurfaceDim)
private val levelContentDescription = context.getString(R.string.keyboard_backlight_value)
@@ -153,11 +151,6 @@ class KeyboardBacklightDialog(
}
}
- @ColorInt
- fun getColorFromStyle(@AttrRes colorId: Int): Int {
- return Utils.getColorAttrDefaultColor(context, colorId)
- }
-
fun updateState(current: Int, max: Int, forceRefresh: Boolean = false) {
if (maxLevel != max || forceRefresh) {
maxLevel = max
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index 84a423e226b4..c3e6f0c6b280 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -21,11 +21,10 @@ import android.view.Surface
import android.view.WindowManager
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
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.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
import javax.inject.Inject
@@ -37,10 +36,10 @@ import kotlinx.coroutines.flow.asStateFlow
class KeyboardDockingIndicationViewModel
@Inject
constructor(
- private val windowManager: WindowManager,
- @Application private val context: Context,
+ @Main private val windowManager: WindowManager,
+ @Main private val context: Context,
keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
- @GlobalConfig configurationInteractor: ConfigurationInteractor,
+ @Main configurationInteractor: ConfigurationInteractor,
@Background private val backgroundScope: CoroutineScope,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/OWNERS b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/OWNERS
new file mode 100644
index 000000000000..2355c48158f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1562219
+chrisgollner@google.com
+jmokut@google.com \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
index 8c393e27da59..b59713696055 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
@@ -21,6 +21,7 @@ import android.hardware.input.InputGestureData
import android.view.KeyboardShortcutGroup
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
/**
* Internal Keyboard Shortcut models to use with [ShortcutCategoriesUtils.fetchShortcutCategory]
@@ -48,10 +49,15 @@ data class InternalKeyboardShortcutGroup(
* @param isCustomShortcut If Shortcut is user customized or system defined.
*/
data class InternalKeyboardShortcutInfo(
- val label: String,
+ val label: String = "",
val keycode: Int,
val modifiers: Int,
val baseCharacter: Char = Char.MIN_VALUE,
val icon: Icon? = null,
val isCustomShortcut: Boolean = false,
)
+
+data class InternalGroupsSource(
+ val groups: List<InternalKeyboardShortcutGroup>,
+ val type: ShortcutCategoryType,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt
new file mode 100644
index 000000000000..b029b03ec8b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.keyboard.shortcut.data.repository
+
+import android.hardware.input.AppLaunchData
+import android.hardware.input.InputGestureData.KeyTrigger
+import android.hardware.input.InputManager
+import android.util.Log
+import android.view.InputDevice
+import com.android.systemui.Flags.shortcutHelperKeyGlyph
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
+
+@SysUISingleton
+class AppLaunchDataRepository
+@Inject
+constructor(
+ private val inputManager: InputManager,
+ @Background private val backgroundScope: CoroutineScope,
+ private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
+ inputDeviceRepository: ShortcutHelperInputDeviceRepository,
+) {
+
+ private val shortcutCommandToAppLaunchDataMap:
+ StateFlow<Map<ShortcutCommandKey, AppLaunchData>> =
+ inputDeviceRepository.activeInputDevice
+ .map { inputDevice ->
+ if (inputDevice == null) {
+ emptyMap()
+ }
+ else{
+ buildCommandToAppLaunchDataMap(inputDevice)
+ }
+ }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.Eagerly,
+ initialValue = mapOf(),
+ )
+
+ fun getAppLaunchDataForShortcutWithCommand(shortcutCommand: ShortcutCommand): AppLaunchData? {
+ val shortcutCommandAsKey = ShortcutCommandKey(shortcutCommand)
+ return shortcutCommandToAppLaunchDataMap.value[shortcutCommandAsKey]
+ }
+
+ private fun buildCommandToAppLaunchDataMap(inputDevice: InputDevice):
+ Map<ShortcutCommandKey, AppLaunchData> {
+ val commandToAppLaunchDataMap =
+ mutableMapOf<ShortcutCommandKey, AppLaunchData>()
+ val appLaunchInputGestures = inputManager.appLaunchBookmarks
+ appLaunchInputGestures.forEach { inputGesture ->
+ val keyGlyphMap =
+ if (shortcutHelperKeyGlyph()) {
+ inputManager.getKeyGlyphMap(inputDevice.id)
+ } else null
+
+ val shortcutCommand =
+ shortcutCategoriesUtils.toShortcutCommand(
+ keyGlyphMap,
+ inputDevice.keyCharacterMap,
+ inputGesture.trigger as KeyTrigger,
+ )
+
+ if (shortcutCommand != null) {
+ commandToAppLaunchDataMap[ShortcutCommandKey(shortcutCommand)] =
+ inputGesture.action.appLaunchData()!!
+ } else {
+ Log.w(
+ TAG,
+ "could not get Shortcut Command. inputGesture: $inputGesture",
+ )
+ }
+ }
+
+ return commandToAppLaunchDataMap
+ }
+
+ private data class ShortcutCommandKey(val keys: List<ShortcutKey>) {
+ constructor(
+ shortcutCommand: ShortcutCommand
+ ) : this(shortcutCommand.keys.sortedBy { it.toString() })
+ }
+
+ private companion object {
+ private const val TAG = "AppLaunchDataRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 4af378646db3..18ca877775df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -16,10 +16,8 @@
package com.android.systemui.keyboard.shortcut.data.repository
-import android.content.Context
import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.Builder
-import android.hardware.input.InputGestureData.KeyTrigger
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent.KeyGestureType
@@ -30,54 +28,39 @@ import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
-import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
-import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
@SysUISingleton
class CustomShortcutCategoriesRepository
@Inject
constructor(
- stateRepository: ShortcutHelperStateRepository,
+ inputDeviceRepository: ShortcutHelperInputDeviceRepository,
@Background private val backgroundScope: CoroutineScope,
- @Background private val bgCoroutineContext: CoroutineContext,
private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
- private val context: Context,
- private val inputGestureMaps: InputGestureMaps,
+ private val inputGestureDataAdapter: InputGestureDataAdapter,
private val customInputGesturesRepository: CustomInputGesturesRepository,
- private val inputManager: InputManager
+ private val inputManager: InputManager,
+ private val appLaunchDataRepository: AppLaunchDataRepository,
) : ShortcutCategoriesRepository {
private val _selectedKeyCombination = MutableStateFlow<KeyCombination?>(null)
private val _shortcutBeingCustomized = mutableStateOf<ShortcutCustomizationRequestInfo?>(null)
- private val activeInputDevice =
- stateRepository.state.map {
- if (it is Active) {
- withContext(bgCoroutineContext) { inputManager.getInputDevice(it.deviceId) }
- } else {
- null
- }
- }
-
val pressedKeys =
_selectedKeyCombination
- .combine(activeInputDevice) { keyCombination, inputDevice ->
+ .combine(inputDeviceRepository.activeInputDevice) { keyCombination, inputDevice ->
if (inputDevice == null || keyCombination == null) {
return@combine emptyList()
} else {
@@ -111,12 +94,14 @@ constructor(
)
override val categories: Flow<List<ShortcutCategory>> =
- combine(activeInputDevice, customInputGesturesRepository.customInputGestures)
- { inputDevice, inputGestures ->
+ combine(
+ inputDeviceRepository.activeInputDevice,
+ customInputGesturesRepository.customInputGestures,
+ ) { inputDevice, inputGestures ->
if (inputDevice == null) {
emptyList()
} else {
- val sources = toInternalGroupSources(inputGestures)
+ val sources = inputGestureDataAdapter.toInternalGroupSources(inputGestures)
val supportedKeyCodes =
shortcutCategoriesUtils.fetchSupportedKeyCodes(
inputDevice.id,
@@ -153,10 +138,10 @@ constructor(
fun buildInputGestureDataForShortcutBeingCustomized(): InputGestureData? {
try {
return Builder()
- .addKeyGestureTypeFromShortcutLabel()
+ .addKeyGestureTypeForShortcutBeingCustomized()
.addTriggerFromSelectedKeyCombination()
+ .addAppLaunchDataFromShortcutBeingCustomized()
.build()
- // TODO(b/379648200) add app launch data after dynamic label/icon mapping implementation
} catch (e: IllegalArgumentException) {
Log.w(TAG, "could not add custom shortcut: $e")
return null
@@ -164,9 +149,10 @@ constructor(
}
private fun retrieveInputGestureDataForShortcutBeingDeleted(): InputGestureData? {
- val keyGestureType = getKeyGestureTypeFromShortcutBeingDeletedLabel()
- return customInputGesturesRepository.retrieveCustomInputGestures()
- .firstOrNull { it.action.keyGestureType() == keyGestureType }
+ val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
+ return customInputGesturesRepository.retrieveCustomInputGestures().firstOrNull {
+ it.action.keyGestureType() == keyGestureType
+ }
}
suspend fun confirmAndSetShortcutCurrentlyBeingCustomized():
@@ -189,8 +175,8 @@ constructor(
return customInputGesturesRepository.resetAllCustomInputGestures()
}
- private fun Builder.addKeyGestureTypeFromShortcutLabel(): Builder {
- val keyGestureType = getKeyGestureTypeFromShortcutBeingCustomizedLabel()
+ private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder {
+ val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
if (keyGestureType == null) {
Log.w(
@@ -199,30 +185,28 @@ constructor(
)
return this
}
-
return setKeyGestureType(keyGestureType)
}
- @KeyGestureType
- private fun getKeyGestureTypeFromShortcutBeingCustomizedLabel(): Int? {
+ private fun Builder.addAppLaunchDataFromShortcutBeingCustomized(): Builder {
val shortcutBeingCustomized =
- getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Add
+ (_shortcutBeingCustomized.value as? SingleShortcutCustomization) ?: return this
- if (shortcutBeingCustomized == null) {
- Log.w(
- TAG,
- "Requested key gesture type from label but shortcut being customized is null",
- )
- return null
+ if (shortcutBeingCustomized.categoryType != ShortcutCategoryType.AppCategories) {
+ return this
}
- return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label]
+ val defaultShortcutCommand = shortcutBeingCustomized.shortcutCommand
+
+ val appLaunchData =
+ appLaunchDataRepository.getAppLaunchDataForShortcutWithCommand(defaultShortcutCommand)
+
+ return if (appLaunchData == null) this else this.setAppLaunchData(appLaunchData)
}
@KeyGestureType
- private fun getKeyGestureTypeFromShortcutBeingDeletedLabel(): Int? {
- val shortcutBeingCustomized =
- getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Delete
+ private fun getKeyGestureTypeForShortcutBeingCustomized(): Int? {
+ val shortcutBeingCustomized = getShortcutBeingCustomized() as? SingleShortcutCustomization
if (shortcutBeingCustomized == null) {
Log.w(
@@ -232,7 +216,10 @@ constructor(
return null
}
- return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label]
+ return inputGestureDataAdapter.getKeyGestureTypeForShortcut(
+ shortcutLabel = shortcutBeingCustomized.label,
+ shortcutCategoryType = shortcutBeingCustomized.categoryType,
+ )
}
private fun Builder.addTriggerFromSelectedKeyCombination(): Builder {
@@ -261,70 +248,6 @@ constructor(
return _shortcutBeingCustomized.value
}
- private fun toInternalGroupSources(
- inputGestures: List<InputGestureData>
- ): List<InternalGroupsSource> {
- val ungroupedInternalGroupSources =
- inputGestures.mapNotNull { gestureData ->
- val keyTrigger = gestureData.trigger as KeyTrigger
- val keyGestureType = gestureData.action.keyGestureType()
- fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
- toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger)?.let {
- internalKeyboardShortcutInfo ->
- val group =
- InternalKeyboardShortcutGroup(
- label = groupLabel,
- items = listOf(internalKeyboardShortcutInfo),
- )
-
- fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
- InternalGroupsSource(groups = listOf(group), type = it)
- }
- }
- }
- }
-
- return ungroupedInternalGroupSources
- }
-
- private fun toInternalKeyboardShortcutInfo(
- keyGestureType: Int,
- keyTrigger: KeyTrigger,
- ): InternalKeyboardShortcutInfo? {
- fetchShortcutInfoLabelByGestureType(keyGestureType)?.let {
- return InternalKeyboardShortcutInfo(
- label = it,
- keycode = keyTrigger.keycode,
- modifiers = keyTrigger.modifierState,
- isCustomShortcut = true,
- )
- }
- return null
- }
-
- private fun fetchGroupLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
- inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
- return context.getString(it)
- } ?: return null
- }
-
- private fun fetchShortcutInfoLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
- inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
- return context.getString(it)
- } ?: return null
- }
-
- private fun fetchShortcutCategoryTypeByGestureType(
- @KeyGestureType keyGestureType: Int
- ): ShortcutCategoryType? {
- return inputGestureMaps.gestureToShortcutCategoryTypeMap[keyGestureType]
- }
-
- private data class InternalGroupsSource(
- val groups: List<InternalKeyboardShortcutGroup>,
- val type: ShortcutCategoryType,
- )
-
private companion object {
private const val TAG = "CustomShortcutCategoriesRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
index 5bb7cdd03b8f..db35d49e7598 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyboard.shortcut.data.repository
-import android.hardware.input.InputManager
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
import com.android.systemui.dagger.SysUISingleton
@@ -36,29 +35,24 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
@SysUISingleton
class DefaultShortcutCategoriesRepository
@Inject
constructor(
@Background private val backgroundScope: CoroutineScope,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
@SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,
@MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,
@AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource,
@InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource,
@CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource,
- private val inputManager: InputManager,
- stateRepository: ShortcutHelperStateRepository,
+ inputDeviceRepository: ShortcutHelperInputDeviceRepository,
shortcutCategoriesUtils: ShortcutCategoriesUtils,
) : ShortcutCategoriesRepository {
@@ -83,17 +77,8 @@ constructor(
),
)
- private val activeInputDevice =
- stateRepository.state.map {
- if (it is Active) {
- withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
- } else {
- null
- }
- }
-
override val categories: Flow<List<ShortcutCategory>> =
- activeInputDevice
+ inputDeviceRepository.activeInputDevice
.map { inputDevice ->
if (inputDevice == null) {
return@map emptyList()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
new file mode 100644
index 000000000000..6e754a37ebca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
@@ -0,0 +1,280 @@
+/*
+ * 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.keyboard.shortcut.data.repository
+
+import android.annotation.SuppressLint
+import android.app.role.RoleManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_MAIN
+import android.content.Intent.CATEGORY_LAUNCHER
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.drawable.Icon
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.CategoryData
+import android.hardware.input.AppLaunchData.ComponentData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.KeyTrigger
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+import android.hardware.input.KeyGestureEvent.KeyGestureType
+import android.util.Log
+import com.android.internal.app.ResolverActivity
+import com.android.systemui.keyboard.shortcut.data.model.InternalGroupsSource
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+
+/**
+ * Serves as a bridge for converting InputGestureData API Models to Shortcut Helper Data Layer
+ * Models and vice versa.
+ */
+class InputGestureDataAdapter
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ private val inputGestureMaps: InputGestureMaps,
+ private val context: Context,
+) {
+ private val userContext: Context
+ get() = userTracker.createCurrentUserContext(userTracker.userContext)
+
+ fun toInternalGroupSources(inputGestures: List<InputGestureData>): List<InternalGroupsSource> {
+ val ungroupedInternalGroupSources =
+ inputGestures.mapNotNull { gestureData ->
+ val keyTrigger = gestureData.trigger as KeyTrigger
+ val keyGestureType = gestureData.action.keyGestureType()
+ val appLaunchData: AppLaunchData? = gestureData.action.appLaunchData()
+ fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
+ toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger, appLaunchData)
+ ?.let { internalKeyboardShortcutInfo ->
+ val group =
+ InternalKeyboardShortcutGroup(
+ label = groupLabel,
+ items = listOf(internalKeyboardShortcutInfo),
+ )
+
+ fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
+ InternalGroupsSource(groups = listOf(group), type = it)
+ }
+ }
+ }
+ }
+
+ return ungroupedInternalGroupSources
+ }
+
+ fun getKeyGestureTypeForShortcut(
+ shortcutLabel: String,
+ shortcutCategoryType: ShortcutCategoryType,
+ ): Int? {
+ if (shortcutCategoryType == ShortcutCategoryType.AppCategories) {
+ return KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+ }
+ val result = inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutLabel]
+ return result
+ }
+
+ private fun toInternalKeyboardShortcutInfo(
+ keyGestureType: Int,
+ keyTrigger: KeyTrigger,
+ appLaunchData: AppLaunchData?,
+ ): InternalKeyboardShortcutInfo? {
+ fetchShortcutLabelByGestureType(keyGestureType, appLaunchData)?.let {
+ return InternalKeyboardShortcutInfo(
+ label = it,
+ keycode = keyTrigger.keycode,
+ modifiers = keyTrigger.modifierState,
+ isCustomShortcut = true,
+ icon = appLaunchData?.let { fetchShortcutIconByAppLaunchData(appLaunchData) },
+ )
+ }
+ return null
+ }
+
+ @SuppressLint("QueryPermissionsNeeded")
+ private fun fetchShortcutIconByAppLaunchData(appLaunchData: AppLaunchData): Icon? {
+ val intent = fetchIntentFromAppLaunchData(appLaunchData) ?: return null
+ val resolvedActivity = resolveSingleMatchingActivityFrom(intent)
+
+ return if (resolvedActivity == null) {
+ null
+ } else {
+ Icon.createWithResource(context, resolvedActivity.iconResource)
+ }
+ }
+
+ private fun fetchGroupLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
+ inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ } ?: return null
+ }
+
+ private fun fetchShortcutLabelByGestureType(
+ @KeyGestureType keyGestureType: Int,
+ appLaunchData: AppLaunchData?,
+ ): String? {
+ inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ }
+
+ if (keyGestureType == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return fetchShortcutLabelByAppLaunchData(appLaunchData!!)
+ }
+
+ return null
+ }
+
+ private fun fetchShortcutLabelByAppLaunchData(appLaunchData: AppLaunchData): String? {
+ val intent = fetchIntentFromAppLaunchData(appLaunchData) ?: return null
+ val resolvedActivity = resolveSingleMatchingActivityFrom(intent)
+
+ return if (resolvedActivity == null) {
+ getIntentCategoryLabel(intent.selector?.categories?.iterator()?.next())
+ } else resolvedActivity.loadLabel(userContext.packageManager).toString()
+ }
+
+ @SuppressLint("QueryPermissionsNeeded")
+ private fun resolveSingleMatchingActivityFrom(intent: Intent): ActivityInfo? {
+ val packageManager = userContext.packageManager
+ val resolvedActivity =
+ intent.resolveActivityInfo(packageManager, /* flags= */ MATCH_DEFAULT_ONLY)
+ ?: return null
+
+ val matchesMultipleActivities =
+ ResolverActivity::class.qualifiedName.equals(resolvedActivity.name)
+
+ return if (matchesMultipleActivities) {
+ return null
+ } else resolvedActivity
+ }
+
+ private fun getIntentCategoryLabel(category: String?): String? {
+ val categoryLabelRes =
+ when (category.toString()) {
+ Intent.CATEGORY_APP_BROWSER -> R.string.keyboard_shortcut_group_applications_browser
+ Intent.CATEGORY_APP_CONTACTS ->
+ R.string.keyboard_shortcut_group_applications_contacts
+ Intent.CATEGORY_APP_EMAIL -> R.string.keyboard_shortcut_group_applications_email
+ Intent.CATEGORY_APP_CALENDAR ->
+ R.string.keyboard_shortcut_group_applications_calendar
+ Intent.CATEGORY_APP_MAPS -> R.string.keyboard_shortcut_group_applications_maps
+ Intent.CATEGORY_APP_MUSIC -> R.string.keyboard_shortcut_group_applications_music
+ Intent.CATEGORY_APP_MESSAGING -> R.string.keyboard_shortcut_group_applications_sms
+ Intent.CATEGORY_APP_CALCULATOR ->
+ R.string.keyboard_shortcut_group_applications_calculator
+ else -> {
+ Log.w(TAG, ("No label for app category $category"))
+ null
+ }
+ }
+
+ return if (categoryLabelRes == null) {
+ return null
+ } else {
+ context.getString(categoryLabelRes)
+ }
+ }
+
+ private fun fetchIntentFromAppLaunchData(appLaunchData: AppLaunchData): Intent? {
+ return when (appLaunchData) {
+ is CategoryData ->
+ Intent.makeMainSelectorActivity(
+ /* selectorAction= */ ACTION_MAIN,
+ /* selectorCategory= */ appLaunchData.category,
+ )
+
+ is RoleData -> getRoleLaunchIntent(appLaunchData.role)
+ is ComponentData ->
+ resolveComponentNameIntent(
+ packageName = appLaunchData.packageName,
+ className = appLaunchData.className,
+ )
+
+ else -> null
+ }
+ }
+
+ private fun resolveComponentNameIntent(packageName: String, className: String): Intent? {
+ buildIntentFromComponentName(ComponentName(packageName, className))?.let {
+ return it
+ }
+ buildIntentFromComponentName(
+ ComponentName(
+ userContext.packageManager
+ .canonicalToCurrentPackageNames(arrayOf(packageName))[0],
+ className,
+ )
+ )
+ ?.let {
+ return it
+ }
+ return null
+ }
+
+ private fun buildIntentFromComponentName(componentName: ComponentName): Intent? {
+ try {
+ val flags =
+ MATCH_DIRECT_BOOT_UNAWARE or MATCH_DIRECT_BOOT_AWARE or MATCH_UNINSTALLED_PACKAGES
+ // attempt to retrieve activity info to see if a NameNotFoundException is thrown.
+ userContext.packageManager.getActivityInfo(componentName, flags)
+ } catch (e: NameNotFoundException) {
+ Log.w(TAG, "Unable to find activity info for componentName: $componentName")
+ return null
+ }
+
+ return Intent(ACTION_MAIN).apply {
+ addCategory(CATEGORY_LAUNCHER)
+ component = componentName
+ }
+ }
+
+ @SuppressLint("NonInjectedService")
+ private fun getRoleLaunchIntent(role: String): Intent? {
+ val packageManager = userContext.packageManager
+ val roleManager = userContext.getSystemService(RoleManager::class.java)!!
+ if (roleManager.isRoleAvailable(role)) {
+ roleManager.getDefaultApplication(role)?.let { rolePackage ->
+ packageManager.getLaunchIntentForPackage(rolePackage)?.let {
+ return it
+ } ?: Log.w(TAG, "No launch intent for role $role")
+ } ?: Log.w(TAG, "No default application for role $role, user= ${userContext.user}")
+ } else {
+ Log.w(TAG, "Role $role is not available.")
+ }
+ return null
+ }
+
+ private fun fetchShortcutCategoryTypeByGestureType(
+ @KeyGestureType keyGestureType: Int
+ ): ShortcutCategoryType? {
+ return inputGestureMaps.gestureToShortcutCategoryTypeMap[keyGestureType]
+ }
+
+ private companion object {
+ private const val TAG = "InputGestureDataUtils"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index 1c380c26c6c3..d7be5e622276 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -22,14 +22,8 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_BACK
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
@@ -74,13 +68,7 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
)
val gestureToInternalKeyboardShortcutGroupLabelResIdMap =
@@ -116,20 +104,14 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
R.string.shortcutHelper_category_split_screen,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION to
R.string.keyboard_shortcut_group_applications,
)
+ /**
+ * App Category shortcut labels are mapped dynamically based on intent
+ * see [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
+ */
val gestureToInternalKeyboardShortcutInfoLabelResIdMap =
mapOf(
// System Category
@@ -154,26 +136,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to R.string.system_multitasking_lhs,
KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to R.string.system_multitasking_rhs,
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to R.string.system_multitasking_full_screen,
- KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to
- R.string.system_multitasking_splitscreen_focus_lhs,
- KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
- R.string.system_multitasking_splitscreen_focus_rhs,
-
- // App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
- R.string.keyboard_shortcut_group_applications_calculator,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
- R.string.keyboard_shortcut_group_applications_calendar,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
- R.string.keyboard_shortcut_group_applications_browser,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
- R.string.keyboard_shortcut_group_applications_contacts,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to
- R.string.keyboard_shortcut_group_applications_email,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to
- R.string.keyboard_shortcut_group_applications_maps,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
- R.string.keyboard_shortcut_group_applications_sms,
)
val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index 27d1a30f4346..cf5460fef0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyboard.shortcut.data.repository
import android.content.Context
import android.graphics.drawable.Icon
+import android.hardware.input.InputGestureData.KeyTrigger
import android.hardware.input.InputManager
import android.hardware.input.KeyGlyphMap
import android.util.Log
@@ -33,6 +34,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperExclusions
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
@@ -46,6 +48,7 @@ constructor(
private val context: Context,
@Background private val backgroundCoroutineContext: CoroutineContext,
private val inputManager: InputManager,
+ private val shortcutHelperExclusions: ShortcutHelperExclusions,
) {
fun removeUnsupportedModifiers(modifierMask: Int): Int {
@@ -135,6 +138,7 @@ constructor(
label = shortcutInfo.label,
icon = toShortcutIcon(keepIcon, shortcutInfo),
commands = listOf(shortcutCommand),
+ isCustomizable = shortcutHelperExclusions.isShortcutCustomizable(shortcutInfo.label),
)
}
@@ -154,6 +158,22 @@ constructor(
return ShortcutIcon(packageName = icon.resPackage, resourceId = icon.resId)
}
+ fun toShortcutCommand(
+ keyGlyphMap: KeyGlyphMap?,
+ keyCharacterMap: KeyCharacterMap,
+ keyTrigger: KeyTrigger,
+ ): ShortcutCommand? {
+ return toShortcutCommand(
+ keyGlyphMap = keyGlyphMap,
+ keyCharacterMap = keyCharacterMap,
+ info =
+ InternalKeyboardShortcutInfo(
+ keycode = keyTrigger.keycode,
+ modifiers = keyTrigger.modifierState,
+ ),
+ )
+ }
+
private fun toShortcutCommand(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt
new file mode 100644
index 000000000000..13613733c2bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.hardware.input.InputManager
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+class ShortcutHelperInputDeviceRepository
+@Inject
+constructor(
+ stateRepository: ShortcutHelperStateRepository,
+ @Background private val backgroundScope: CoroutineScope,
+ @Background private val bgCoroutineContext: CoroutineContext,
+ private val inputManager: InputManager,
+) {
+ val activeInputDevice =
+ stateRepository.state
+ .map {
+ if (it is Active) {
+ withContext(bgCoroutineContext) { inputManager.getInputDevice(it.deviceId) }
+ } else {
+ null
+ }
+ }
+ .stateIn(scope = backgroundScope, started = SharingStarted.Lazily, initialValue = null)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index df6b04e2afd3..d785b5b5a7e7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -26,7 +26,6 @@ import android.view.KeyEvent.KEYCODE_EQUALS
import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
import android.view.KeyEvent.KEYCODE_MINUS
import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
-import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyboardShortcutGroup
@@ -74,20 +73,6 @@ constructor(@Main private val resources: Resources, @Application private val con
command(META_META_ON or META_CTRL_ON, KEYCODE_DPAD_UP)
}
)
- // Change split screen focus to RHS:
- // - Meta + Alt + Right arrow
- add(
- shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
- command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_RIGHT)
- }
- )
- // Change split screen focus to LHS:
- // - Meta + Alt + Left arrow
- add(
- shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_lhs)) {
- command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_LEFT)
- }
- )
if (enableMoveToNextDisplayShortcut()) {
// Move a window to the next display:
// - Meta + Ctrl + D
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index d62584644743..61d11f4df5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -92,9 +92,7 @@ constructor(
.groupBy { it.label }
.entries
.map { (commonLabel, groupedShortcuts) ->
- Shortcut(
- label = commonLabel,
- icon = groupedShortcuts.firstOrNull()?.icon,
+ groupedShortcuts[0].copy(
commands = groupedShortcuts.flatMap { it.commands }.sortedBy { it.keys.size },
contentDescription =
toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }),
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
index 9cc15ce809e9..55cc8e0905b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -21,6 +21,7 @@ data class Shortcut(
val commands: List<ShortcutCommand>,
val icon: ShortcutIcon? = null,
val contentDescription: String = "",
+ val isCustomizable: Boolean = true,
) {
val containsCustomShortcutCommands: Boolean = commands.any { it.isCustom }
}
@@ -28,6 +29,7 @@ data class Shortcut(
class ShortcutBuilder(private val label: String) {
val commands = mutableListOf<ShortcutCommand>()
var contentDescription = ""
+ var isCustomizable = true
fun command(builder: ShortcutCommandBuilder.() -> Unit) {
commands += ShortcutCommandBuilder().apply(builder).build()
@@ -37,7 +39,13 @@ class ShortcutBuilder(private val label: String) {
contentDescription = string.invoke()
}
- fun build() = Shortcut(label, commands, contentDescription = contentDescription)
+ fun build() =
+ Shortcut(
+ label,
+ commands,
+ contentDescription = contentDescription,
+ isCustomizable = isCustomizable,
+ )
}
fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index c7e6b43b9624..d8bad2590280 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -18,7 +18,10 @@ package com.android.systemui.keyboard.shortcut.shared.model
import androidx.annotation.DrawableRes
-data class ShortcutCommand(val keys: List<ShortcutKey>, val isCustom: Boolean = false)
+data class ShortcutCommand(
+ val keys: List<ShortcutKey> = emptyList(),
+ val isCustom: Boolean = false,
+)
class ShortcutCommandBuilder {
private val keys = mutableListOf<ShortcutKey>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
index 095de41237cf..f183247bb355 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
@@ -17,17 +17,27 @@
package com.android.systemui.keyboard.shortcut.shared.model
sealed interface ShortcutCustomizationRequestInfo {
- data class Add(
- val label: String = "",
- val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
- val subCategoryLabel: String = "",
- ) : ShortcutCustomizationRequestInfo
- data class Delete(
- val label: String = "",
- val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
- val subCategoryLabel: String = "",
- ) : ShortcutCustomizationRequestInfo
+ sealed interface SingleShortcutCustomization: ShortcutCustomizationRequestInfo {
+ val label: String
+ val categoryType: ShortcutCategoryType
+ val subCategoryLabel: String
+ val shortcutCommand: ShortcutCommand
+
+ data class Add(
+ override val label: String = "",
+ override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
+ override val subCategoryLabel: String = "",
+ override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
+ ) : SingleShortcutCustomization
+
+ data class Delete(
+ override val label: String = "",
+ override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
+ override val subCategoryLabel: String = "",
+ override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
+ ) : SingleShortcutCustomization
+ }
data object Reset : ShortcutCustomizationRequestInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt
new file mode 100644
index 000000000000..20b74c86973f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+import android.content.Context
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class ShortcutHelperExclusions @Inject constructor(private val context: Context) {
+ private val nonCustomizableShortcutsLabels: List<String>
+ get() =
+ listOf(
+ context.getString(R.string.group_system_cycle_forward),
+ context.getString(R.string.group_system_cycle_back),
+ )
+
+ fun isShortcutCustomizable(label: String) = !nonCustomizableShortcutsLabels.contains(label)
+}
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 2fdcf8767ae5..aea583d67289 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
@@ -189,7 +189,7 @@ private fun ActiveShortcutHelper(
onKeyboardSettingsClicked,
shortcutsUiState.isShortcutCustomizerFlagEnabled,
onCustomizationRequested,
- shortcutsUiState.shouldShowResetButton
+ shortcutsUiState.shouldShowResetButton,
)
}
}
@@ -380,7 +380,7 @@ private fun ShortcutHelperTwoPane(
onKeyboardSettingsClicked: () -> Unit,
isShortcutCustomizerFlagEnabled: Boolean,
onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
- shouldShowResetButton: Boolean
+ shouldShowResetButton: Boolean,
) {
val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
var isCustomizing by remember { mutableStateOf(false) }
@@ -505,10 +505,10 @@ private fun EndSidePanel(
isCustomizing = isCustomizing and category.type.includeInCustomization,
onCustomizationRequested = { requestInfo ->
when (requestInfo) {
- is ShortcutCustomizationRequestInfo.Add ->
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
onCustomizationRequested(requestInfo.copy(categoryType = category.type))
- is ShortcutCustomizationRequestInfo.Delete ->
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
onCustomizationRequested(requestInfo.copy(categoryType = category.type))
ShortcutCustomizationRequestInfo.Reset ->
@@ -565,15 +565,15 @@ private fun SubCategoryContainerDualPane(
modifier = Modifier.padding(vertical = 8.dp),
searchQuery = searchQuery,
shortcut = shortcut,
- isCustomizing = isCustomizing,
+ isCustomizing = isCustomizing && shortcut.isCustomizable,
onCustomizationRequested = { requestInfo ->
when (requestInfo) {
- is ShortcutCustomizationRequestInfo.Add ->
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
onCustomizationRequested(
requestInfo.copy(subCategoryLabel = subCategory.label)
)
- is ShortcutCustomizationRequestInfo.Delete ->
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
onCustomizationRequested(
requestInfo.copy(subCategoryLabel = subCategory.label)
)
@@ -615,7 +615,7 @@ private fun Shortcut(
}
.focusable(interactionSource = interactionSource)
.padding(8.dp)
- .semantics { contentDescription = shortcut.contentDescription }
+ .semantics(mergeDescendants = true) { contentDescription = shortcut.contentDescription }
) {
Row(
modifier =
@@ -644,12 +644,18 @@ private fun Shortcut(
isCustomizing = isCustomizing,
onAddShortcutRequested = {
onCustomizationRequested(
- ShortcutCustomizationRequestInfo.Add(label = shortcut.label)
+ ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add(
+ label = shortcut.label,
+ shortcutCommand = shortcut.commands.first(),
+ )
)
},
onDeleteShortcutRequested = {
onCustomizationRequested(
- ShortcutCustomizationRequestInfo.Delete(label = shortcut.label)
+ ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete(
+ label = shortcut.label,
+ shortcutCommand = shortcut.commands.first(),
+ )
)
},
)
@@ -801,7 +807,10 @@ private fun ShortcutKeyContainer(shortcutKeyContent: @Composable BoxScope.() ->
private fun BoxScope.ShortcutTextKey(key: ShortcutKey.Text) {
Text(
text = key.value,
- modifier = Modifier.align(Alignment.Center).padding(horizontal = 12.dp),
+ modifier =
+ Modifier.align(Alignment.Center).padding(horizontal = 12.dp).semantics {
+ hideFromAccessibility()
+ },
style = MaterialTheme.typography.titleSmall,
)
}
@@ -825,7 +834,7 @@ private fun FlowRowScope.ShortcutOrSeparator(spacing: Dp) {
Spacer(Modifier.width(spacing))
Text(
text = stringResource(R.string.shortcut_helper_key_combinations_or_separator),
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier = Modifier.align(Alignment.CenterVertically).semantics { hideFromAccessibility() },
style = MaterialTheme.typography.titleSmall,
)
Spacer(Modifier.width(spacing))
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 92e25929fe4f..373eb250d61d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -66,8 +66,9 @@ constructor(
}
fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
+ shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
when (requestInfo) {
- is ShortcutCustomizationRequestInfo.Add -> {
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add -> {
_shortcutCustomizationUiState.value =
AddShortcutDialog(
shortcutLabel = requestInfo.label,
@@ -75,12 +76,10 @@ constructor(
shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
pressedKeys = emptyList(),
)
- shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
}
- is ShortcutCustomizationRequestInfo.Delete -> {
+ is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete -> {
_shortcutCustomizationUiState.value = DeleteShortcutDialog
- shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
}
ShortcutCustomizationRequestInfo.Reset -> {
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 0101e099084e..4370abf9ce5a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -62,6 +62,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransitionModule;
+import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -111,6 +113,7 @@ import java.util.concurrent.Executor;
includes = {
DeviceEntryIconTransitionModule.class,
FalsingModule.class,
+ PrimaryBouncerTransitionModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardQuickAffordancesCombinedViewModelModule.class,
KeyguardRepositoryModule.class,
@@ -248,4 +251,10 @@ public interface KeyguardModule {
@IntoMap
@ClassKey(KeyguardUpdateMonitor.class)
CoreStartable bindsKeyguardUpdateMonitor(KeyguardUpdateMonitor keyguardUpdateMonitor);
+
+ /***/
+ @Binds
+ @IntoMap
+ @ClassKey(AlternateBouncerWindowViewBinder.class)
+ CoreStartable bindsAlternateBouncerWindowViewBinder(AlternateBouncerWindowViewBinder binder);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index d1f9fa259c6b..e8d3bfac6361 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -27,7 +27,6 @@ import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.ZenModeConfig
import android.util.Log
import com.android.settingslib.notification.modes.EnableZenModeDialog
-import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModeDialogMetricsLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -99,15 +98,6 @@ constructor(
private var oldIsAvailable = false
private var settingsValue: Int = 0
- private val dndMode: StateFlow<ZenMode?> by lazy {
- ModesUi.assertInNewMode()
- interactor.dndMode.stateIn(
- scope = backgroundScope,
- started = SharingStarted.Eagerly,
- initialValue = null,
- )
- }
-
private val isAvailable: StateFlow<Boolean> by lazy {
ModesUi.assertInNewMode()
interactor.isZenAvailable.stateIn(
@@ -146,7 +136,7 @@ constructor(
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
if (ModesUi.isEnabled) {
- combine(isAvailable, dndMode) { isAvailable, dndMode ->
+ combine(isAvailable, interactor.dndMode) { isAvailable, dndMode ->
if (!isAvailable) {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else if (dndMode?.isActive == true) {
@@ -222,7 +212,7 @@ constructor(
if (!isAvailable.value) {
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
} else {
- val dnd = dndMode.value
+ val dnd = interactor.dndMode.value
if (dnd == null) {
Log.wtf(TAG, "Triggered DND but it's null!?")
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index 71f29c0062bc..d335a1806a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -20,12 +20,12 @@ import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.util.Log
-import com.android.systemui.Flags.glanceableHubShortcutButton
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.communal.data.repository.CommunalSceneRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +46,7 @@ constructor(
@Application private val context: Context,
private val communalSceneRepository: CommunalSceneRepository,
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val sceneInteractor: SceneInteractor,
) : KeyguardQuickAffordanceConfig {
@@ -61,8 +62,7 @@ constructor(
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
get() = flow {
emit(
- // TODO(b/378113263): Gate on getV2FlagEnabled() when ready.
- if (!glanceableHubShortcutButton()) {
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
Log.i(TAG, "Button hidden on lockscreen: flag not enabled.")
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else if (!communalInteractor.isCommunalEnabled.value) {
@@ -81,8 +81,7 @@ constructor(
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
- // TODO(b/378113263): Gate on getV2FlagEnabled() when ready.
- return if (!glanceableHubShortcutButton()) {
+ return if (!communalSettingsInteractor.isV2FlagEnabled()) {
Log.i(TAG, "Button unavailable in picker: flag not enabled.")
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
} else if (!communalInteractor.isCommunalEnabled.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index d04e4f1171a0..7c8bca8d9eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -17,8 +17,11 @@
package com.android.systemui.keyguard.domain.interactor
import android.graphics.Point
+import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.power.data.repository.PowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
@@ -30,6 +33,7 @@ class DozeInteractor
@Inject
constructor(
private val keyguardRepository: KeyguardRepository,
+ private val powerRepository: PowerRepository,
// TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
// while the flag is off, creating this object too early results in a crash
private val sceneInteractor: Lazy<SceneInteractor>,
@@ -41,6 +45,20 @@ constructor(
return sceneInteractor.get().currentScene.value == Scenes.Lockscreen
}
+ fun setDozeScreenState(state: Int) {
+ powerRepository.dozeScreenState.value =
+ when (state) {
+ Display.STATE_UNKNOWN -> DozeScreenStateModel.UNKNOWN
+ Display.STATE_OFF -> DozeScreenStateModel.OFF
+ Display.STATE_ON -> DozeScreenStateModel.ON
+ Display.STATE_DOZE -> DozeScreenStateModel.DOZE
+ Display.STATE_DOZE_SUSPEND -> DozeScreenStateModel.DOZE_SUSPEND
+ Display.STATE_VR -> DozeScreenStateModel.VR
+ Display.STATE_ON_SUSPEND -> DozeScreenStateModel.ON_SUSPEND
+ else -> throw IllegalArgumentException("Invalid DozeScreenState: $state")
+ }
+ }
+
fun setAodAvailable(value: Boolean) {
keyguardRepository.setAodAvailable(value)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index deef2a6c3a4c..a9992112f893 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -21,7 +21,6 @@ import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -49,7 +48,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.filter
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -178,7 +176,8 @@ constructor(
newScene = CommunalScenes.Communal,
loggingReason = "FromDreamingTransitionInteractor",
transitionKey =
- if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade
+ if (communalSettingsInteractor.isV2FlagEnabled())
+ CommunalTransitionKeys.SimpleFade
else null,
)
} else {
@@ -226,8 +225,15 @@ constructor(
scope.launch {
keyguardInteractor.isAbleToDream
- .filter { !it }
- .sample(deviceEntryInteractor.isUnlocked, ::Pair)
+ .filterRelevantKeyguardStateAnd { !it }
+ .sample(
+ if (SceneContainerFlag.isEnabled) {
+ deviceEntryInteractor.isUnlocked
+ } else {
+ keyguardInteractor.isKeyguardDismissible
+ },
+ ::Pair,
+ )
.collect { (_, dismissable) ->
// TODO(b/349837588): Add check for -> OCCLUDED.
if (dismissable) {
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 26c286df01d7..0d9474e07ce7 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
@@ -200,7 +200,10 @@ constructor(
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
*
- * Allow a brief moment to prevent rapidly oscillating between true/false signals.
+ * Allow a brief moment to prevent rapidly oscillating between true/false signals. The amount of
+ * time is [IS_ABLE_TO_DREAM_DELAY_MS] - consumers should consider waiting for that long before
+ * examining the value of this flow, to let other consumers have enough time to also see that
+ * same new value.
*/
val isAbleToDream: Flow<Boolean> =
dozeTransitionModel
@@ -212,7 +215,7 @@ constructor(
// do not immediately process any dreaming information when exiting AOD. It
// should actually be quite strange to leave AOD and then go straight to
// DREAMING so this should be fine.
- delay(500L)
+ delay(IS_ABLE_TO_DREAM_DELAY_MS)
isDreaming
.sample(powerInteractor.isAwake) { isDreaming, isAwake ->
isDreaming && isAwake
@@ -550,5 +553,11 @@ constructor(
companion object {
private const val TAG = "KeyguardInteractor"
+ /**
+ * Amount of time that [KeyguardInteractor.isAbleToDream] is delayed; consumers of that flow
+ * should consider waiting this amount of time before check the value of this flow, to let
+ * other consumers have enough time to see the new value.
+ */
+ const val IS_ABLE_TO_DREAM_DELAY_MS = 500L
}
}
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 7cd2744cb7dc..f078fe26902e 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
@@ -256,6 +256,16 @@ constructor(
val isTransitioningBetweenDesiredScenes =
sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
+ // When in STL A -> B settles in A we can't do the same in KTF as KTF requires us to
+ // start B -> A to get back to A. [LockscreenSceneTransitionInteractor] will emit these
+ // steps but because STL is Idle(A) at this point (and never even started a B -> A in
+ // the first place) [isTransitioningBetweenDesiredScenes] will not be satisfied. We need
+ // this condition to not filter out the STARTED and FINISHED step of the "artificially"
+ // reversed B -> A transition.
+ val belongsToInstantReversedTransition =
+ sceneInteractor.transitionState.value.isIdle(toScene) &&
+ sceneTransitionPair.value.previousValue.isTransitioning(toScene, fromScene)
+
// We can't compare the terminal step with the current sceneTransition because
// a) STL has no guarantee that it will settle in Idle() when finished/canceled
// b) Comparing to Idle(toScene) would make any other FINISHED step settling in
@@ -267,7 +277,8 @@ constructor(
return@filter isTransitioningBetweenLockscreenStates ||
isTransitioningBetweenDesiredScenes ||
- terminalStepBelongsToPreviousTransition
+ terminalStepBelongsToPreviousTransition ||
+ belongsToInstantReversedTransition
}
}
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 9df293bf2c15..3b99bb4c40fa 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
@@ -276,7 +276,11 @@ constructor(
false
} else if (
currentState == KeyguardState.DREAMING &&
- deviceEntryInteractor.get().isUnlocked.value
+ if (SceneContainerFlag.isEnabled) {
+ deviceEntryInteractor.get().isUnlocked.value
+ } else {
+ keyguardInteractor.isKeyguardDismissible.value
+ }
) {
// Dreams dismiss keyguard and return to GONE if they can.
false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 0cb8dd4798fa..5c03d65e570f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -36,7 +36,6 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
/**
@@ -51,17 +50,11 @@ constructor(
private val logger: KeyguardTransitionAnimationLogger,
) {
/** Invoke once per transition between FROM->TO states to get access to a shared flow. */
- fun setup(
- duration: Duration,
- edge: Edge,
- ): FlowBuilder {
+ fun setup(duration: Duration, edge: Edge): FlowBuilder {
return FlowBuilder(duration, edge)
}
- inner class FlowBuilder(
- private val transitionDuration: Duration,
- private val edge: Edge,
- ) {
+ inner class FlowBuilder(private val transitionDuration: Duration, private val edge: Edge) {
fun setupWithoutSceneContainer(edge: Edge.StateToState): FlowBuilder {
if (SceneContainerFlag.isEnabled) return this
return setup(this.transitionDuration, edge)
@@ -72,6 +65,8 @@ constructor(
* in the range of [0, 1]. View animations should begin and end within a subset of this
* range. This function maps the [startTime] and [duration] into [0, 1], when this subset is
* valid.
+ *
+ * Note that [onCancel] isn't used when the scene framework is enabled.
*/
fun sharedFlow(
duration: Duration,
@@ -81,7 +76,7 @@ constructor(
onCancel: (() -> Float)? = null,
onFinish: (() -> Float)? = null,
interpolator: Interpolator = LINEAR,
- name: String? = null
+ name: String? = null,
): Flow<Float> {
return sharedFlowWithState(
duration = duration,
@@ -113,7 +108,7 @@ constructor(
onCancel: (() -> Float)? = null,
onFinish: (() -> Float)? = null,
interpolator: Interpolator = LINEAR,
- name: String? = null
+ name: String? = null,
): Flow<StateToValue> {
if (!duration.isPositive()) {
throw IllegalArgumentException("duration must be a positive number: $duration")
@@ -155,20 +150,40 @@ constructor(
return transitionInteractor
.transition(edge)
- .map { step ->
- StateToValue(
- from = step.from,
- to = step.to,
- transitionState = step.transitionState,
- value =
- when (step.transitionState) {
- STARTED -> stepToValue(step)
- RUNNING -> stepToValue(step)
- CANCELED -> onCancel?.invoke()
- FINISHED -> onFinish?.invoke()
- }
- )
- .also { logger.logTransitionStep(name, step, it.value) }
+ .mapNotNull { step ->
+ if (SceneContainerFlag.isEnabled && step.transitionState == CANCELED) {
+ // When the scene framework is enabled, there's no need to emit an alpha
+ // value when the keyguard transition animation is canceled because there's
+ // always going to be a new, reversed keyguard transition animation back to
+ // the original KeyguardState that starts right when this one was canceled.
+ //
+ // For example, if swiping up slightly on the Lockscreen scene and then
+ // releasing before the transition to the Bouncer scene is committed, the
+ // KTF transition of LOCKSCREEN -> PRIMARY_BOUNCER received a CANCELED and
+ // the scene framework immediately starts a reversed transition of
+ // PRIMARY_BOUNCER -> LOCKSCREEN, which picks up where the previous one left
+ // off.
+ //
+ // If it were allowed for the CANCELED from the original KTF transition to
+ // emit a value, a race condition could form where the value from CANCELED
+ // arrives downstream _after_ the reversed transition is finished, causing
+ // the transition to end up in an incorrect state at rest.
+ null
+ } else {
+ StateToValue(
+ from = step.from,
+ to = step.to,
+ transitionState = step.transitionState,
+ value =
+ when (step.transitionState) {
+ STARTED -> stepToValue(step)
+ RUNNING -> stepToValue(step)
+ CANCELED -> onCancel?.invoke()
+ FINISHED -> onFinish?.invoke()
+ },
+ )
+ .also { logger.logTransitionStep(name, step, it.value) }
+ }
}
.distinctUntilChanged()
}
@@ -181,7 +196,7 @@ constructor(
duration = 1.milliseconds,
onStep = { value },
onCancel = { value },
- onFinish = { value }
+ onFinish = { value },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 7a368999ecc9..33783b515763 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -16,9 +16,7 @@
package com.android.systemui.keyguard.ui.binder
-import android.graphics.PixelFormat
import android.util.Log
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -36,6 +34,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewLayoutParams
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
@@ -68,28 +67,6 @@ constructor(
private val windowManager: Lazy<WindowManager>,
private val layoutInflater: Lazy<LayoutInflater>,
) : CoreStartable {
- private val layoutParams: WindowManager.LayoutParams
- get() =
- WindowManager.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT,
- )
- .apply {
- title = "AlternateBouncerView"
- fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
- gravity = Gravity.TOP or Gravity.LEFT
- layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- privateFlags =
- WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
- WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
- // Avoid announcing window title.
- accessibilityTitle = " "
- }
private var alternateBouncerView: ConstraintLayout? = null
@@ -176,7 +153,9 @@ constructor(
as ConstraintLayout
Log.d(TAG, "Adding alternate bouncer view")
- windowManager.get().addView(alternateBouncerView, layoutParams)
+ windowManager
+ .get()
+ .addView(alternateBouncerView, AlternateBouncerWindowViewLayoutParams.layoutParams)
alternateBouncerView!!.addOnAttachStateChangeListener(onAttachAddBackGestureHandler)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3bdf7dac75b3..c59fe5357ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.binder
import android.annotation.SuppressLint
+import android.content.res.ColorStateList
import android.graphics.Rect
import android.graphics.drawable.Animatable2
import android.util.Size
@@ -37,7 +38,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.settingslib.Utils
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableLinearLayout
@@ -382,25 +382,25 @@ object KeyguardBottomAreaViewBinder {
view.isActivated = viewModel.isActivated
view.drawable.setTint(
- Utils.getColorAttrDefaultColor(
- view.context,
+ view.context.getColor(
if (viewModel.isActivated) {
- com.android.internal.R.attr.materialColorOnPrimaryFixed
+ com.android.internal.R.color.materialColorOnPrimaryFixed
} else {
- com.android.internal.R.attr.materialColorOnSurface
- },
+ com.android.internal.R.color.materialColorOnSurface
+ }
)
)
view.backgroundTintList =
if (!viewModel.isSelected) {
- Utils.getColorAttr(
- view.context,
- if (viewModel.isActivated) {
- com.android.internal.R.attr.materialColorPrimaryFixed
- } else {
- com.android.internal.R.attr.materialColorSurfaceContainerHigh
- }
+ ColorStateList.valueOf(
+ view.context.getColor(
+ if (viewModel.isActivated) {
+ com.android.internal.R.color.materialColorPrimaryFixed
+ } else {
+ com.android.internal.R.color.materialColorSurfaceContainerHigh
+ }
+ )
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 8cae77756721..0a958e9d06a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -27,7 +27,6 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.ClockSize
@@ -73,7 +72,6 @@ object KeyguardClockViewBinder {
// When changing to new clock, we need to remove old views from burnInLayer
var lastClock: ClockController? = null
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.currentClock.collect { currentClock ->
if (lastClock != currentClock) {
cleanupClockViews(
@@ -99,7 +97,6 @@ object KeyguardClockViewBinder {
}
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.clockSize.collect { clockSize ->
updateBurnInLayer(keyguardRootView, viewModel, clockSize)
blueprintInteractor.refreshBlueprint(Type.ClockSize)
@@ -107,7 +104,6 @@ object KeyguardClockViewBinder {
}
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.clockShouldBeCentered.collect {
viewModel.currentClock.value?.let {
// TODO(b/301502635): remove "!it.config.useCustomClockScene" when
@@ -125,7 +121,6 @@ object KeyguardClockViewBinder {
}
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
combine(
viewModel.hasAodIcons,
rootViewModel.isNotifIconContainerVisible.map { it.value },
@@ -143,7 +138,6 @@ object KeyguardClockViewBinder {
}
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
aodBurnInViewModel.movement.collect { burnInModel ->
viewModel.currentClock.value
?.largeClock
@@ -159,7 +153,6 @@ object KeyguardClockViewBinder {
}
launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.largeClockTextSize.collect { fontSizePx ->
viewModel.currentClock.value
?.largeClock
@@ -170,6 +163,18 @@ object KeyguardClockViewBinder {
}
}
+ disposables +=
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.currentClock.collect { currentClock ->
+ currentClock?.apply {
+ smallClock.run { events.onThemeChanged(theme) }
+ largeClock.run { events.onThemeChanged(theme) }
+ }
+ }
+ }
+ }
+
return disposables
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index c0b3d8345249..1964cb2f811f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -136,8 +136,16 @@ object KeyguardPreviewClockViewBinder {
viewModel: KeyguardPreviewClockViewModel,
) {
val cs = ConstraintSet().apply { clone(rootView) }
- previewClock.largeClock.layout.applyPreviewConstraints(clockPreviewConfig, cs)
- previewClock.smallClock.layout.applyPreviewConstraints(clockPreviewConfig, cs)
+
+ val configWithUpdatedLockId =
+ if (rootView.getViewById(lockId) != null) {
+ clockPreviewConfig.copy(lockId = lockId)
+ } else {
+ clockPreviewConfig
+ }
+
+ previewClock.largeClock.layout.applyPreviewConstraints(configWithUpdatedLockId, cs)
+ previewClock.smallClock.layout.applyPreviewConstraints(configWithUpdatedLockId, cs)
// When selectedClockSize is the initial value, make both clocks invisible to avoid
// flickering
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 191e08b0de77..5c8a234ec6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.binder
import android.annotation.SuppressLint
+import android.content.res.ColorStateList
import android.graphics.drawable.Animatable2
import android.util.Size
import android.view.View
@@ -32,7 +33,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
-import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
@@ -176,25 +176,25 @@ constructor(
view.isActivated = viewModel.isActivated
view.drawable.setTint(
- Utils.getColorAttrDefaultColor(
- view.context,
+ view.context.getColor(
if (viewModel.isActivated) {
- com.android.internal.R.attr.materialColorOnPrimaryFixed
+ com.android.internal.R.color.materialColorOnPrimaryFixed
} else {
- com.android.internal.R.attr.materialColorOnSurface
+ com.android.internal.R.color.materialColorOnSurface
},
)
)
view.backgroundTintList =
if (!viewModel.isSelected) {
- Utils.getColorAttr(
- view.context,
- if (viewModel.isActivated) {
- com.android.internal.R.attr.materialColorPrimaryFixed
- } else {
- com.android.internal.R.attr.materialColorSurfaceContainerHigh
- }
+ ColorStateList.valueOf(
+ view.context.getColor(
+ if (viewModel.isActivated) {
+ com.android.internal.R.color.materialColorPrimaryFixed
+ } else {
+ com.android.internal.R.color.materialColorSurfaceContainerHigh
+ }
+ )
)
} else {
null
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 f121aabe795a..cd4d9dcf366c 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
@@ -31,14 +31,12 @@ import android.view.View.OnLayoutChangeListener
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
-import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
@@ -56,7 +54,6 @@ import com.android.systemui.customization.R as customR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.KeyguardViewMediator
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -90,12 +87,9 @@ import kotlin.math.min
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -161,21 +155,19 @@ object KeyguardRootViewBinder {
disposables +=
view.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- if (MigrateClocksToBlueprint.isEnabled) {
- launch("$TAG#topClippingBounds") {
- val clipBounds = Rect()
- viewModel.topClippingBounds.collect { clipTop ->
- if (clipTop == null) {
- view.setClipBounds(null)
- } else {
- clipBounds.apply {
- top = clipTop
- left = view.getLeft()
- right = view.getRight()
- bottom = view.getBottom()
- }
- view.setClipBounds(clipBounds)
+ launch("$TAG#topClippingBounds") {
+ val clipBounds = Rect()
+ viewModel.topClippingBounds.collect { clipTop ->
+ if (clipTop == null) {
+ view.setClipBounds(null)
+ } else {
+ clipBounds.apply {
+ top = clipTop
+ left = view.getLeft()
+ right = view.getRight()
+ bottom = view.getBottom()
}
+ view.setClipBounds(clipBounds)
}
}
}
@@ -190,40 +182,36 @@ object KeyguardRootViewBinder {
}
}
- if (MigrateClocksToBlueprint.isEnabled) {
- launch("$TAG#translationY") {
- // When translation happens in burnInLayer, it won't be weather clock
- // large clock isn't added to burnInLayer due to its scale transition
- // so we also need to add translation to it here
- // same as translationX
- viewModel.translationY.collect { y ->
- childViews[burnInLayerId]?.translationY = y
- childViews[largeClockId]?.translationY = y
- childViews[aodNotificationIconContainerId]?.translationY = y
- }
+ launch("$TAG#translationY") {
+ // When translation happens in burnInLayer, it won't be weather clock large
+ // clock isn't added to burnInLayer due to its scale transition so we also
+ // need to add translation to it here same as translationX
+ viewModel.translationY.collect { y ->
+ childViews[burnInLayerId]?.translationY = y
+ childViews[largeClockId]?.translationY = y
+ childViews[aodNotificationIconContainerId]?.translationY = y
}
+ }
- launch("$TAG#translationX") {
- viewModel.translationX.collect { state ->
- val px = state.value ?: return@collect
- when {
- state.isToOrFrom(KeyguardState.AOD) -> {
- // Large Clock is not translated in the x direction
- childViews[burnInLayerId]?.translationX = px
- childViews[aodNotificationIconContainerId]?.translationX =
- px
- }
- state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
- for ((key, childView) in childViews.entries) {
- when (key) {
- indicationArea,
- startButton,
- endButton,
- deviceEntryIcon -> {
- // Do not move these views
- }
- else -> childView.translationX = px
+ launch("$TAG#translationX") {
+ viewModel.translationX.collect { state ->
+ val px = state.value ?: return@collect
+ when {
+ state.isToOrFrom(KeyguardState.AOD) -> {
+ // Large Clock is not translated in the x direction
+ childViews[burnInLayerId]?.translationX = px
+ childViews[aodNotificationIconContainerId]?.translationX = px
+ }
+ state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+ for ((key, childView) in childViews.entries) {
+ when (key) {
+ indicationArea,
+ startButton,
+ endButton,
+ deviceEntryIcon -> {
+ // Do not move these views
}
+ else -> childView.translationX = px
}
}
}
@@ -263,95 +251,92 @@ object KeyguardRootViewBinder {
}
}
- if (MigrateClocksToBlueprint.isEnabled) {
- launch {
- viewModel.burnInLayerVisibility.collect { visibility ->
- childViews[burnInLayerId]?.visibility = visibility
- }
+ launch {
+ viewModel.burnInLayerVisibility.collect { visibility ->
+ childViews[burnInLayerId]?.visibility = visibility
}
+ }
- launch {
- viewModel.burnInLayerAlpha.collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
+ launch {
+ viewModel.burnInLayerAlpha.collect { alpha ->
+ childViews[statusViewId]?.alpha = alpha
}
+ }
- launch {
- viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
+ launch {
+ viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
+ childViews[statusViewId]?.alpha = alpha
}
+ }
- launch {
- viewModel.scale.collect { scaleViewModel ->
- if (scaleViewModel.scaleClockOnly) {
- // For clocks except weather clock, we have scale transition
- // besides translate
- childViews[largeClockId]?.let {
- it.scaleX = scaleViewModel.scale
- it.scaleY = scaleViewModel.scale
- }
+ launch {
+ viewModel.scale.collect { scaleViewModel ->
+ if (scaleViewModel.scaleClockOnly) {
+ // For clocks except weather clock, we have scale transition besides
+ // translate
+ childViews[largeClockId]?.let {
+ it.scaleX = scaleViewModel.scale
+ it.scaleY = scaleViewModel.scale
}
}
}
+ }
- launch {
- blueprintViewModel.currentTransition.collect { currentTransition ->
- // When blueprint/clock transitions end (null), make sure NSSL is in
- // the right place
- if (currentTransition == null) {
- childViews[nsslPlaceholderId]?.let { notificationListPlaceholder
- ->
- viewModel.onNotificationContainerBoundsChanged(
- notificationListPlaceholder.top.toFloat(),
- notificationListPlaceholder.bottom.toFloat(),
- animate = true,
- )
- }
+ launch {
+ blueprintViewModel.currentTransition.collect { currentTransition ->
+ // When blueprint/clock transitions end (null), make sure NSSL is in the
+ // right place
+ if (currentTransition == null) {
+ childViews[nsslPlaceholderId]?.let { notificationListPlaceholder ->
+ viewModel.onNotificationContainerBoundsChanged(
+ notificationListPlaceholder.top.toFloat(),
+ notificationListPlaceholder.bottom.toFloat(),
+ animate = true,
+ )
}
}
}
+ }
- launch {
- val iconsAppearTranslationPx =
- configuration
- .getDimensionPixelSize(R.dimen.shelf_appear_translation)
- .stateIn(this)
- viewModel.isNotifIconContainerVisible.collect { isVisible ->
- if (isVisible.value) {
- blueprintViewModel.refreshBlueprint()
- }
- childViews[aodNotificationIconContainerId]
- ?.setAodNotifIconContainerIsVisible(
- isVisible,
- iconsAppearTranslationPx.value,
- screenOffAnimationController,
- )
+ launch {
+ val iconsAppearTranslationPx =
+ configuration
+ .getDimensionPixelSize(R.dimen.shelf_appear_translation)
+ .stateIn(this)
+ viewModel.isNotifIconContainerVisible.collect { isVisible ->
+ if (isVisible.value) {
+ blueprintViewModel.refreshBlueprint()
}
+ childViews[aodNotificationIconContainerId]
+ ?.setAodNotifIconContainerIsVisible(
+ isVisible,
+ iconsAppearTranslationPx.value,
+ screenOffAnimationController,
+ )
}
+ }
- interactionJankMonitor?.let { jankMonitor ->
- launch {
- viewModel.goneToAodTransition.collect {
- when (it.transitionState) {
- TransitionState.STARTED -> {
- val clockId = clockInteractor.renderedClockId
- val builder =
- InteractionJankMonitor.Configuration.Builder
- .withView(CUJ_SCREEN_OFF_SHOW_AOD, view)
- .setTag(clockId)
- jankMonitor.begin(builder)
- }
- TransitionState.CANCELED ->
- jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
- TransitionState.FINISHED -> {
- if (MigrateClocksToBlueprint.isEnabled) {
- keyguardViewMediator?.maybeHandlePendingLock()
- }
- jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
- }
- TransitionState.RUNNING -> Unit
+ interactionJankMonitor?.let { jankMonitor ->
+ launch {
+ viewModel.goneToAodTransition.collect {
+ when (it.transitionState) {
+ TransitionState.STARTED -> {
+ val clockId = clockInteractor.renderedClockId
+ val builder =
+ InteractionJankMonitor.Configuration.Builder.withView(
+ CUJ_SCREEN_OFF_SHOW_AOD,
+ view,
+ )
+ .setTag(clockId)
+ jankMonitor.begin(builder)
+ }
+ TransitionState.CANCELED ->
+ jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+ TransitionState.FINISHED -> {
+ keyguardViewMediator?.maybeHandlePendingLock()
+ jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
}
+ TransitionState.RUNNING -> Unit
}
}
}
@@ -406,13 +391,11 @@ object KeyguardRootViewBinder {
}
}
- if (MigrateClocksToBlueprint.isEnabled) {
- burnInParams.update { current ->
- current.copy(
- translationX = { childViews[burnInLayerId]?.translationX },
- translationY = { childViews[burnInLayerId]?.translationY },
- )
- }
+ burnInParams.update { current ->
+ current.copy(
+ translationX = { childViews[burnInLayerId]?.translationX },
+ translationY = { childViews[burnInLayerId]?.translationY },
+ )
}
disposables +=
@@ -515,20 +498,16 @@ object KeyguardRootViewBinder {
burnInParams.update { current ->
current.copy(
minViewY =
- if (MigrateClocksToBlueprint.isEnabled) {
- // To ensure burn-in doesn't enroach the top inset, get the min top Y
- childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
- min(
- currentMin,
- if (!isUserVisible(view)) {
- Int.MAX_VALUE
- } else {
- view.getTop()
- },
- )
- }
- } else {
- childViews[statusViewId]?.top ?: 0
+ // To ensure burn-in doesn't enroach the top inset, get the min top Y
+ childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
+ min(
+ currentMin,
+ if (!isUserVisible(view)) {
+ Int.MAX_VALUE
+ } else {
+ view.getTop()
+ },
+ )
}
)
}
@@ -542,28 +521,6 @@ object KeyguardRootViewBinder {
}
}
- suspend fun bindAodNotifIconVisibility(
- view: View,
- isVisible: Flow<AnimatedValue<Boolean>>,
- configuration: ConfigurationState,
- screenOffAnimationController: ScreenOffAnimationController,
- ) {
- if (MigrateClocksToBlueprint.isEnabled) {
- throw IllegalStateException("should only be called in legacy code paths")
- }
- coroutineScope {
- val iconAppearTranslationPx =
- configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
- isVisible.collect { isVisible ->
- view.setAodNotifIconContainerIsVisible(
- isVisible = isVisible,
- iconsAppearTranslationPx = iconAppearTranslationPx.value,
- screenOffAnimationController = screenOffAnimationController,
- )
- }
- }
- }
-
private fun View.setAodNotifIconContainerIsVisible(
isVisible: AnimatedValue<Boolean>,
iconsAppearTranslationPx: Int,
@@ -578,9 +535,6 @@ object KeyguardRootViewBinder {
}
when {
!isVisible.isAnimating -> {
- if (!MigrateClocksToBlueprint.isEnabled) {
- translationY = 0f
- }
visibility =
if (isVisible.value) {
alpha = 1f
@@ -591,7 +545,6 @@ object KeyguardRootViewBinder {
}
}
else -> {
- animateInIconTranslation()
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
} else {
@@ -601,19 +554,10 @@ object KeyguardRootViewBinder {
}
}
- private fun View.animateInIconTranslation() {
- if (!MigrateClocksToBlueprint.isEnabled) {
- animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
- }
- }
-
private fun MotionEvent.isTouchscreenSource(): Boolean {
return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
}
- private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
- setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
-
private val statusViewId = R.id.keyguard_status_view
private val burnInLayerId = R.id.burn_in_layer
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index de4a1b03203c..213083db71c9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -22,7 +22,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
@@ -44,13 +43,12 @@ object KeyguardSmartspaceViewBinder {
return keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch("$TAG#clockViewModel.hasCustomWeatherDataDisplay") {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
->
updateDateWeatherToBurnInLayer(
keyguardRootView,
clockViewModel,
- smartspaceViewModel
+ smartspaceViewModel,
)
blueprintInteractor.refreshBlueprint(
Config(
@@ -63,7 +61,6 @@ object KeyguardSmartspaceViewBinder {
}
launch("$TAG#smartspaceViewModel.bcSmartspaceVisibility") {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
smartspaceViewModel.bcSmartspaceVisibility.collect {
updateBCSmartspaceInBurnInLayer(keyguardRootView, clockViewModel)
blueprintInteractor.refreshBlueprint(
@@ -100,7 +97,7 @@ object KeyguardSmartspaceViewBinder {
private fun updateDateWeatherToBurnInLayer(
keyguardRootView: ConstraintLayout,
clockViewModel: KeyguardClockViewModel,
- smartspaceViewModel: KeyguardSmartspaceViewModel
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
if (clockViewModel.hasCustomWeatherDataDisplay.value) {
removeDateWeatherFromBurnInLayer(keyguardRootView, smartspaceViewModel)
@@ -112,7 +109,7 @@ object KeyguardSmartspaceViewBinder {
private fun addDateWeatherToBurnInLayer(
constraintLayout: ConstraintLayout,
- smartspaceViewModel: KeyguardSmartspaceViewModel
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
burnInLayer.apply {
@@ -129,7 +126,7 @@ object KeyguardSmartspaceViewBinder {
private fun removeDateWeatherFromBurnInLayer(
constraintLayout: ConstraintLayout,
- smartspaceViewModel: KeyguardSmartspaceViewModel
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
burnInLayer.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 7d77e7160d83..85725d24758d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -368,8 +368,8 @@ constructor(
SceneContainerFlag.isEnabled,
)
)
- val startPadding: Int = smartspaceViewModel.getSmartspaceStartPadding(previewContext)
- val endPadding: Int = smartspaceViewModel.getSmartspaceEndPadding(previewContext)
+ val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext)
+ val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
@@ -680,6 +680,7 @@ constructor(
}
// In clock preview, we should have a seed color for clock
// before setting clock to clockEventController to avoid updateColor with seedColor == null
+ // So in update colors, it should already have the correct theme in clockFaceController
if (MigrateClocksToBlueprint.isEnabled) {
clockController.clock = clock
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
new file mode 100644
index 000000000000..cafc90930432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.transitions
+
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Each PrimaryBouncerTransition is responsible for updating various UI states based on the nature
+ * of the transition.
+ *
+ * MUST list implementing classes in dagger module [PrimaryBouncerTransitionModule].
+ */
+interface PrimaryBouncerTransition {
+ /** Radius of blur applied to the window's root view. */
+ val windowBlurRadius: Flow<Float>
+
+ companion object {
+ const val MAX_BACKGROUND_BLUR_RADIUS = 150f
+ const val MIN_BACKGROUND_BLUR_RADIUS = 0f
+ }
+}
+
+/**
+ * Module that installs all the transitions from different keyguard states to and away from the
+ * primary bouncer.
+ */
+@ExperimentalCoroutinesApi
+@Module
+interface PrimaryBouncerTransitionModule {
+ @Binds
+ @IntoSet
+ fun fromAod(impl: AodToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun fromAlternateBouncer(
+ impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
+ ): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun fromDozing(impl: DozingToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun fromLockscreen(
+ impl: LockscreenToPrimaryBouncerTransitionViewModel
+ ): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun toAod(impl: PrimaryBouncerToAodTransitionViewModel): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun toLockscreen(impl: PrimaryBouncerToLockscreenTransitionViewModel): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun toDozing(impl: PrimaryBouncerToDozingTransitionViewModel): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun toGlanceableHub(
+ impl: PrimaryBouncerToGlanceableHubTransitionViewModel
+ ): PrimaryBouncerTransition
+
+ @Binds
+ @IntoSet
+ fun toGone(impl: PrimaryBouncerToGoneTransitionViewModel): PrimaryBouncerTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt
new file mode 100644
index 000000000000..d3bc79eb89e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.ui.view
+
+import android.content.Context
+import android.view.View
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.compose.ui.platform.ComposeView
+import com.android.systemui.CoreStartable
+import com.android.systemui.compose.ComposeInitializer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.ui.composable.AlternateBouncer
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Drives the showing and hiding of the alternate bouncer window. */
+@SysUISingleton
+class AlternateBouncerWindowViewBinder
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Application private val context: Context,
+ private val viewModel: AlternateBouncerViewModel,
+ private val dependencies: AlternateBouncerDependencies,
+ private val windowManager: WindowManager,
+) : CoreStartable {
+
+ override fun start() {
+ if (!SceneContainerFlag.isEnabled) {
+ return
+ }
+
+ applicationScope.launch {
+ viewModel.isVisible
+ .distinctUntilChanged()
+ .filter { it }
+ .collect {
+ windowManager.addView(
+ createView(),
+ AlternateBouncerWindowViewLayoutParams.layoutParams,
+ )
+ }
+ }
+ }
+
+ private fun createView(): View {
+ val root = FrameLayout(context)
+ val composeView =
+ ComposeView(context).apply {
+ setContent {
+ AlternateBouncer(
+ alternateBouncerDependencies = dependencies,
+ onHideAnimationFinished = {
+ if (root.isAttachedToWindow) {
+ windowManager.removeView(root)
+ }
+ },
+ )
+ }
+ }
+
+ root.repeatWhenAttached {
+ root.repeatWhenAttachedToWindow {
+ try {
+ ComposeInitializer.onAttachedToWindow(root)
+ root.addView(composeView)
+ awaitCancellation()
+ } finally {
+ root.removeView(composeView)
+ ComposeInitializer.onDetachedFromWindow(root)
+ }
+ }
+ }
+
+ return root
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt
new file mode 100644
index 000000000000..5585c21aa217
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view
+
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.WindowManager
+
+object AlternateBouncerWindowViewLayoutParams {
+ val layoutParams: WindowManager.LayoutParams
+ get() =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT,
+ )
+ .apply {
+ title = "AlternateBouncerView"
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ // Avoid announcing window title.
+ accessibilityTitle = " "
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 4c23adfe92e8..6f7872c9cb96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -30,7 +30,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.res.R
@@ -62,9 +61,6 @@ constructor(
private lateinit var nic: NotificationIconContainer
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
nic =
NotificationIconContainer(context, null).apply {
id = nicId
@@ -81,10 +77,6 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
nicBindingDisposable?.dispose()
nicBindingDisposable =
NotificationIconContainerViewBinder.bindWhileAttached(
@@ -98,10 +90,6 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
val isVisible = rootViewModel.isNotifIconContainerVisible.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index d54d411b7de6..73e14b1524f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -33,8 +33,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.res.R as R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import dagger.Lazy
@@ -113,8 +113,9 @@ constructor(
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
- val horizontalPaddingStart = KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
- val horizontalPaddingEnd = KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
+ val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
+ val smartspaceHorizontalPadding =
+ KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
constraintSet.apply {
// migrate addDateWeatherView, addWeatherView from KeyguardClockSwitchController
constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
@@ -124,7 +125,7 @@ constructor(
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart,
+ dateWeatherPaddingStart,
)
// migrate addSmartspaceView from KeyguardClockSwitchController
@@ -135,7 +136,7 @@ constructor(
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart,
+ smartspaceHorizontalPadding,
)
connect(
sharedR.id.bc_smartspace_view,
@@ -143,7 +144,7 @@ constructor(
if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline
else ConstraintSet.PARENT_ID,
ConstraintSet.END,
- horizontalPaddingEnd,
+ smartspaceHorizontalPadding,
)
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 85ce5cd0f9fd..5b23e4796fae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
@@ -38,7 +42,10 @@ import kotlinx.coroutines.flow.Flow
@SysUISingleton
class AlternateBouncerToPrimaryBouncerTransitionViewModel
@Inject
-constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
@@ -65,4 +72,18 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val windowBlurRadius: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.sharedFlow(
+ duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ onStep = { step ->
+ MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, step)
+ },
+ onFinish = { MAX_BACKGROUND_BLUR_RADIUS },
+ ),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 35f05f55caa1..e6b796fb92c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -23,6 +23,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,19 +38,19 @@ import kotlinx.coroutines.flow.Flow
@SysUISingleton
class AodToPrimaryBouncerTransitionViewModel
@Inject
-constructor(
- animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+ DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
edge = Edge.create(from = AOD, to = Scenes.Bouncer),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val windowBlurRadius: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index 7ddf641e9e8e..c1670c3814dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,9 +40,8 @@ import kotlinx.coroutines.flow.Flow
@SysUISingleton
class DozingToPrimaryBouncerTransitionViewModel
@Inject
-constructor(
- animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+ DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
@@ -46,10 +49,17 @@ constructor(
duration = TO_PRIMARY_BOUNCER_DURATION,
edge = Edge.create(from = DOZING, to = Scenes.Bouncer),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val windowBlurRadius: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ TO_PRIMARY_BOUNCER_DURATION,
+ onStep = { step ->
+ MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, step)
+ },
+ onFinish = { MAX_BACKGROUND_BLUR_RADIUS },
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index 0280d17f4ae2..15b696e71164 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -62,12 +62,12 @@ constructor(
overrideClockSize.value = clockSize
}
- fun getSmartspaceStartPadding(context: Context): Int {
- return KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
+ fun getDateWeatherStartPadding(context: Context): Int {
+ return KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
}
- fun getSmartspaceEndPadding(context: Context): Int {
- return KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
+ fun getDateWeatherEndPadding(context: Context): Int {
+ return KeyguardSmartspaceViewModel.getDateWeatherEndMargin(context)
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 3266dc45427a..5ee80a7b7442 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -94,14 +94,19 @@ constructor(
val isShadeLayoutWide: StateFlow<Boolean> = shadeInteractor.isShadeLayoutWide
companion object {
- fun getSmartspaceStartMargin(context: Context): Int {
+ fun getDateWeatherStartMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
- fun getSmartspaceEndMargin(context: Context): Int {
+ fun getDateWeatherEndMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) +
context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
+
+ fun getSmartspaceHorizontalMargin(context: Context): Int {
+ return context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_horizontal) +
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ }
}
}
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 914730e1ea4a..48cc8ad5321a 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
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
@@ -42,7 +46,7 @@ class LockscreenToPrimaryBouncerTransitionViewModel
constructor(
shadeDependentFlows: ShadeDependentFlows,
animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
@@ -78,4 +82,16 @@ constructor(
),
flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
)
+ override val windowBlurRadius: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.sharedFlow(
+ duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ onStep = {
+ MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, it)
+ },
+ ),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 501feca8c4f7..f14144e36fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
@@ -24,6 +25,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -43,16 +47,14 @@ class PrimaryBouncerToAodTransitionViewModel
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
edge = Edge.create(from = Scenes.Bouncer, to = AOD),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD))
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
@@ -60,7 +62,7 @@ constructor(
val lockscreenAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
- onStep = { it }
+ onStep = { it },
)
override val deviceEntryParentViewAlpha: Flow<Float> =
@@ -77,4 +79,13 @@ constructor(
emptyFlow()
}
}
+
+ override val windowBlurRadius: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+ onStep = { step ->
+ MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, step)
+ },
+ onFinish = { MIN_BACKGROUND_BLUR_RADIUS },
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index e5bb46432226..a24ed264e4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,7 +43,7 @@ class PrimaryBouncerToDozingTransitionViewModel
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
@@ -50,9 +51,7 @@ constructor(
duration = TO_DOZING_DURATION,
edge = Edge.create(from = Scenes.Bouncer, to = DOZING),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING))
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
@@ -66,4 +65,9 @@ constructor(
emptyFlow()
}
}
+
+ override val windowBlurRadius: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(
+ PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
index 9ec15dcff5f4..b52a3905a263 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
@@ -23,13 +23,16 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@SysUISingleton
class PrimaryBouncerToGlanceableHubTransitionViewModel
@Inject
-constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+ DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(duration = TO_GLANCEABLE_HUB_DURATION, edge = Edge.INVALID)
@@ -37,4 +40,7 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
+
+ override val windowBlurRadius: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(MIN_BACKGROUND_BLUR_RADIUS)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 17c678e79d8b..713ac1527d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -16,16 +16,21 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_SHORT_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
import javax.inject.Inject
@@ -48,16 +53,11 @@ constructor(
keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
bouncerToGoneFlows: BouncerToGoneFlows,
animationFlow: KeyguardTransitionAnimationFlow,
-) {
+) : PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
- .setup(
- duration = TO_GONE_DURATION,
- edge = Edge.INVALID,
- )
- .setupWithoutSceneContainer(
- edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
- )
+ .setup(duration = TO_GONE_DURATION, edge = Edge.INVALID)
+ .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE))
private var leaveShadeOpen: Boolean = false
private var willRunDismissFromKeyguard: Boolean = false
@@ -96,7 +96,7 @@ constructor(
private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
return transitionAnimation.sharedFlow(
- duration = 200.milliseconds,
+ duration = TO_GONE_SHORT_DURATION,
onStart = { willRunDismissFromKeyguard = willRunAnimationOnKeyguard() },
onStep = {
if (willRunDismissFromKeyguard) {
@@ -108,6 +108,22 @@ constructor(
)
}
+ private fun createBouncerWindowBlurFlow(
+ willRunAnimationOnKeyguard: () -> Boolean
+ ): Flow<Float> {
+ return transitionAnimation.sharedFlow(
+ duration = TO_GONE_SHORT_DURATION,
+ onStart = { willRunDismissFromKeyguard = willRunAnimationOnKeyguard() },
+ onStep = {
+ if (willRunDismissFromKeyguard) {
+ MIN_BACKGROUND_BLUR_RADIUS
+ } else {
+ MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, it)
+ }
+ },
+ )
+ }
+
/** Lockscreen alpha */
val lockscreenAlpha: Flow<Float> =
if (ComposeBouncerFlags.isEnabled) {
@@ -137,6 +153,16 @@ constructor(
)
}
+ override val windowBlurRadius: Flow<Float> =
+ if (ComposeBouncerFlags.isEnabled) {
+ keyguardDismissActionInteractor
+ .get()
+ .willAnimateDismissActionOnLockscreen
+ .flatMapLatest { createBouncerWindowBlurFlow { it } }
+ } else {
+ createBouncerWindowBlurFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
+ }
+
val scrimAlpha: Flow<ScrimAlpha> =
bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, PRIMARY_BOUNCER)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index d29f5129bd59..e737fcebe211 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -25,6 +25,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -41,22 +44,21 @@ class PrimaryBouncerToLockscreenTransitionViewModel
@Inject
constructor(
animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN))
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = 250.milliseconds,
interpolator = EMPHASIZED_ACCELERATE,
- onStep = { it }
+ onStep = { it },
)
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
@@ -72,4 +74,17 @@ constructor(
transitionAnimation.immediatelyTransitionTo(1f)
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
+
+ override val windowBlurRadius: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.sharedFlow(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ onStep = {
+ MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, it)
+ },
+ ),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 341b8d87eeef..1b39d55d1f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -30,14 +30,10 @@ import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_SEEN_EVENT
import com.android.systemui.media.controls.util.SmallHash
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.time.SystemClock
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.Locale
import java.util.TreeMap
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -49,37 +45,9 @@ class MediaFilterRepository
constructor(
@Application private val applicationContext: Context,
private val systemClock: SystemClock,
- private val configurationController: ConfigurationController,
private val smartspaceLogger: MediaSmartspaceLogger,
) {
- val onAnyMediaConfigurationChange: Flow<Unit> = conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
- trySend(Unit)
- }
-
- override fun onThemeChanged() {
- trySend(Unit)
- }
-
- override fun onUiModeChanged() {
- trySend(Unit)
- }
-
- override fun onLocaleListChanged() {
- if (locale != applicationContext.resources.configuration.locales.get(0)) {
- locale = applicationContext.resources.configuration.locales.get(0)
- trySend(Unit)
- }
- }
- }
- configurationController.addCallback(callback)
- trySend(Unit)
- awaitClose { configurationController.removeCallback(callback) }
- }
-
/** Instance id of media control that recommendations card reactivated. */
private val _reactivatedId: MutableStateFlow<InstanceId?> = MutableStateFlow(null)
val reactivatedId: StateFlow<InstanceId?> = _reactivatedId.asStateFlow()
@@ -190,7 +158,7 @@ constructor(
fun addMediaDataLoadingState(
mediaDataLoadingModel: MediaDataLoadingModel,
- isUpdate: Boolean = true
+ isUpdate: Boolean = true,
) {
val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
sortedMap.putAll(
@@ -395,7 +363,7 @@ constructor(
logSmarspaceRecommendationCardUserEvent(
SMARTSPACE_CARD_SEEN_EVENT,
surface,
- visibleIndex
+ visibleIndex,
)
}
}
@@ -409,7 +377,7 @@ constructor(
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
instanceId: InstanceId? = null,
- isRec: Boolean = false
+ isRec: Boolean = false,
) {
_currentMedia.value.forEachIndexed { index, mediaCommonModel ->
when (mediaCommonModel) {
@@ -423,7 +391,7 @@ constructor(
surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
interactedSubCardRank,
- interactedSubCardCardinality
+ interactedSubCardCardinality,
)
}
return
@@ -437,7 +405,7 @@ constructor(
surface,
index,
interactedSubCardRank,
- interactedSubCardCardinality
+ interactedSubCardCardinality,
)
}
return
@@ -459,14 +427,14 @@ constructor(
SMARTSPACE_CARD_DISMISS_EVENT,
surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
- isSwipeToDismiss = true
+ isSwipeToDismiss = true,
)
is MediaCommonModel.MediaRecommendations ->
logSmarspaceRecommendationCardUserEvent(
SMARTSPACE_CARD_DISMISS_EVENT,
surface,
index,
- isSwipeToDismiss = true
+ isSwipeToDismiss = true,
)
}
}
@@ -513,7 +481,7 @@ constructor(
isReactivated: Boolean,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
- isSwipeToDismiss: Boolean = false
+ isSwipeToDismiss: Boolean = false,
) {
_selectedUserEntries.value[instanceId]?.let {
smartspaceLogger.logSmartspaceCardUIEvent(
@@ -537,7 +505,7 @@ constructor(
index: Int,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
- isSwipeToDismiss: Boolean = false
+ isSwipeToDismiss: Boolean = false,
) {
smartspaceLogger.logSmartspaceCardUIEvent(
eventId,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 1f339dddd4d1..09aa85b74d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -68,8 +68,6 @@ constructor(
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
- val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
-
fun removeMediaControl(
token: MediaSession.Token?,
instanceId: InstanceId,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
index c3a36b258842..48ed3915dedd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
@@ -66,14 +66,12 @@ constructor(
.distinctUntilChanged()
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
- val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
-
fun removeMediaRecommendations(
key: String,
dismissIntent: Intent?,
delayMs: Long,
eventId: Int,
- location: Int
+ location: Int,
) {
logSmartspaceCardUserEvent(eventId, location)
mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
@@ -101,7 +99,7 @@ constructor(
eventId: Int,
location: Int,
interactedSubCardRank: Int,
- interactedSubCardCardinality: Int
+ interactedSubCardCardinality: Int,
) {
if (interactedSubCardRank == -1) {
logSmartspaceCardUserEvent(eventId, MediaSmartspaceLogger.getSurface(location))
@@ -111,7 +109,7 @@ constructor(
MediaSmartspaceLogger.getSurface(location),
interactedSubCardRank = interactedSubCardRank,
interactedSubCardCardinality = interactedSubCardCardinality,
- isRec = true
+ isRec = true,
)
}
if (shouldActivityOpenInForeground(intent)) {
@@ -121,7 +119,7 @@ constructor(
0 /* delay */,
expandable.activityTransitionController(
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER
- )
+ ),
)
} else {
// Otherwise, open the activity in background directly.
@@ -133,7 +131,7 @@ constructor(
repository.logSmartspaceCardUserEvent(
eventId,
MediaSmartspaceLogger.getSurface(location),
- isRec = true
+ isRec = true,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index a6e1582d4e0c..910d3a84aeae 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -34,6 +34,7 @@ import android.widget.ImageButton
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.widget.AdaptiveIcon
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -64,7 +65,6 @@ import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig
import com.android.systemui.surfaceeffects.ripple.RippleShader
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.collectLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
private const val TAG = "MediaControlViewBinder"
@@ -85,7 +85,7 @@ object MediaControlViewBinder {
launch {
viewModel.player.collectLatest { player ->
player?.let {
- if (viewModel.isNewPlayer(it)) {
+ if (viewModel.setPlayer(it)) {
bindMediaCard(
viewHolder,
viewController,
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 43c20117b6e0..f0f8a9592b6f 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
@@ -37,6 +37,7 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DiffUtil
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
@@ -115,7 +116,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
private const val TAG = "MediaCarouselController"
@@ -752,7 +752,11 @@ constructor(
}
}
- private fun onAdded(commonViewModel: MediaCommonViewModel, position: Int) {
+ private fun onAdded(
+ commonViewModel: MediaCommonViewModel,
+ position: Int,
+ configChanged: Boolean = false,
+ ) {
val viewController = mediaViewControllerFactory.get()
viewController.sizeChangedListener = this::updateCarouselDimensions
val lp =
@@ -763,12 +767,13 @@ constructor(
when (commonViewModel) {
is MediaCommonViewModel.MediaControl -> {
val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- if (SceneContainerFlag.isEnabled) {
- viewController.widthInSceneContainerPx = widthInSceneContainerPx
- viewController.heightInSceneContainerPx = heightInSceneContainerPx
- }
+ viewController.widthInSceneContainerPx = widthInSceneContainerPx
+ viewController.heightInSceneContainerPx = heightInSceneContainerPx
viewController.attachPlayer(viewHolder)
viewController.mediaViewHolder?.player?.layoutParams = lp
+ if (configChanged) {
+ commonViewModel.controlViewModel.onMediaConfigChanged()
+ }
MediaControlViewBinder.bind(
viewHolder,
commonViewModel.controlViewModel,
@@ -1271,23 +1276,14 @@ constructor(
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
if (recreateMedia) {
mediaContent.removeAllViews()
- commonViewModels.forEach { viewModel ->
+ commonViewModels.forEachIndexed { index, viewModel ->
when (viewModel) {
- is MediaCommonViewModel.MediaControl -> {
- controllerById[viewModel.instanceId.toString()]?.let {
- it.widthInSceneContainerPx = widthInSceneContainerPx
- it.heightInSceneContainerPx = heightInSceneContainerPx
- mediaContent.addView(it.mediaViewHolder?.player)
- }
- }
- is MediaCommonViewModel.MediaRecommendations -> {
- controllerById[viewModel.key]?.let {
- it.widthInSceneContainerPx = widthInSceneContainerPx
- it.heightInSceneContainerPx = heightInSceneContainerPx
- mediaContent.addView(it.recommendationViewHolder?.recommendations)
- }
- }
+ is MediaCommonViewModel.MediaControl ->
+ controllerById[viewModel.instanceId.toString()]?.onDestroy()
+ is MediaCommonViewModel.MediaRecommendations ->
+ controllerById[viewModel.key]?.onDestroy()
}
+ onAdded(viewModel, index, configChanged = true)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 70ca82492775..dccf61d4e6c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -1369,8 +1369,9 @@ public class MediaControlPanel {
boolean visible = mediaAction != null && !shouldBeHiddenDueToScrubbing;
int notVisibleValue;
- if ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
- || (buttonId == R.id.actionNext && semanticActions.getReserveNext())) {
+ if (!shouldBeHiddenDueToScrubbing
+ && ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
+ || (buttonId == R.id.actionNext && semanticActions.getReserveNext()))) {
notVisibleValue = ConstraintSet.INVISIBLE;
mMediaViewHolder.getAction(buttonId).setFocusable(visible);
mMediaViewHolder.getAction(buttonId).setClickable(visible);
@@ -1408,7 +1409,9 @@ public class MediaControlPanel {
// The scrubbing time views replace the SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING action views,
// so we should only allow scrubbing times to be shown if those action views are present.
return semanticActions != null && SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch(
- id -> semanticActions.getActionById(id) != null
+ id -> (semanticActions.getActionById(id) != null
+ || ((id == R.id.actionPrev && semanticActions.getReservePrev())
+ || (id == R.id.actionNext && semanticActions.getReserveNext())))
);
}
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 4173d2aa272e..61e4d95a88e6 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
@@ -41,10 +41,8 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.res.R
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -56,15 +54,9 @@ class MediaControlViewModel(
private val interactor: MediaControlInteractor,
private val logger: MediaUiEventLogger,
) {
-
- @OptIn(ExperimentalCoroutinesApi::class)
val player: Flow<MediaPlayerViewModel?> =
- interactor.onAnyMediaConfigurationChange
- .flatMapLatest {
- interactor.mediaControl.map { mediaControl ->
- mediaControl?.let { toViewModel(it) }
- }
- }
+ interactor.mediaControl
+ .map { mediaControl -> mediaControl?.let { toViewModel(it) } }
.distinctUntilChanged { old, new ->
(new == null && old == null) || new?.contentEquals(old) ?: false
}
@@ -74,14 +66,21 @@ class MediaControlViewModel(
private var isAnyButtonClicked = false
@MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
private var playerViewModel: MediaPlayerViewModel? = null
+ private var allowPlayerUpdate: Boolean = false
+
+ fun setPlayer(viewModel: MediaPlayerViewModel): Boolean {
+ val tempViewModel = playerViewModel
+ playerViewModel = viewModel
+ return allowPlayerUpdate || !(tempViewModel?.contentEquals(viewModel) ?: false)
+ }
- fun isNewPlayer(viewModel: MediaPlayerViewModel): Boolean {
- val contentEquals = playerViewModel?.contentEquals(viewModel) ?: false
- return (!contentEquals).also { playerViewModel = viewModel }
+ fun onMediaConfigChanged() {
+ allowPlayerUpdate = true
}
fun onMediaControlIsBound(artistName: CharSequence, titleName: CharSequence) {
interactor.logMediaControlIsBound(artistName, titleName)
+ allowPlayerUpdate = false
}
private fun onDismissMediaData(
@@ -317,8 +316,11 @@ class MediaControlViewModel(
isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
notVisibleValue =
if (
- (buttonId == R.id.actionPrev && model.semanticActionButtons!!.reservePrev) ||
- (buttonId == R.id.actionNext && model.semanticActionButtons!!.reserveNext)
+ !shouldHideWhenScrubbing &&
+ ((buttonId == R.id.actionPrev &&
+ model.semanticActionButtons!!.reservePrev) ||
+ (buttonId == R.id.actionNext &&
+ model.semanticActionButtons!!.reserveNext))
) {
ConstraintSet.INVISIBLE
} else {
@@ -383,7 +385,9 @@ class MediaControlViewModel(
// so we should only allow scrubbing times to be shown if those action views are present.
return semanticActions?.let {
SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
- semanticActions.getActionById(id) != null
+ semanticActions.getActionById(id) != null ||
+ (id == R.id.actionPrev && semanticActions.reservePrev ||
+ id == R.id.actionNext && semanticActions.reserveNext)
}
} ?: false
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 6bc6b10a1dfd..88cfbaf00987 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -41,10 +41,8 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -59,12 +57,9 @@ constructor(
private val logger: MediaUiEventLogger,
) {
- @OptIn(ExperimentalCoroutinesApi::class)
val mediaRecsCard: Flow<MediaRecsCardViewModel?> =
- interactor.onAnyMediaConfigurationChange
- .flatMapLatest {
- interactor.recommendations.map { recsCard -> toRecsViewModel(recsCard) }
- }
+ interactor.recommendations
+ .map { recsCard -> toRecsViewModel(recsCard) }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 2a9fe8314349..1e99697e6a33 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -93,7 +93,7 @@ constructor(
scrubbing = false,
elapsedTime = null,
duration = 0,
- listening = false
+ listening = false,
)
set(value) {
val enabledChanged = value.enabled != field.enabled
@@ -135,7 +135,6 @@ constructor(
override fun onMetadataChanged(metadata: MediaMetadata?) {
if (!Flags.mediaControlsPostsOptimization()) return
-
val (enabled, duration) = getEnabledStateAndDuration(metadata)
if (_data.duration != duration) {
_data = _data.copy(enabled = enabled, duration = duration)
@@ -323,7 +322,7 @@ constructor(
bgExecutor.executeRepeatedly(
this::checkPlaybackPosition,
0L,
- POSITION_UPDATE_INTERVAL_MILLIS
+ POSITION_UPDATE_INTERVAL_MILLIS,
)
cancel = Runnable {
cancelPolling.run()
@@ -331,6 +330,7 @@ constructor(
}
}
} else {
+ checkPlaybackPosition()
cancel?.run()
cancel = null
}
@@ -542,7 +542,7 @@ constructor(
eventStart: MotionEvent?,
event: MotionEvent,
distanceX: Float,
- distanceY: Float
+ distanceY: Float,
): Boolean {
return shouldGoToSeekBar
}
@@ -556,7 +556,7 @@ constructor(
eventStart: MotionEvent?,
event: MotionEvent,
velocityX: Float,
- velocityY: Float
+ velocityY: Float,
): Boolean {
if (Math.abs(velocityX) > flingVelocity || Math.abs(velocityY) > flingVelocity) {
viewModel.onSeekFalse()
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
index 95b8fa74feeb..4976d94d9057 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 1280508
-# Files in this directory should still be reviewed by a member of SystemUI team
+asapperstein@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 2f0e1298499c..f8d317a7799f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -19,12 +19,12 @@ package com.android.systemui.media.taptotransfer.common
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
-import com.android.systemui.res.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.res.R
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
/** Utility methods for media tap-to-transfer. */
@@ -108,7 +108,7 @@ class MediaTttUtils {
data class IconInfo(
val contentDescription: ContentDescription,
val icon: MediaTttIcon,
- @AttrRes val tint: Int?,
+ @ColorRes val tint: Int?,
/**
* True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
*/
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 1204cde19c76..2a23620839e5 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
@@ -74,7 +74,7 @@ constructor(
context: Context,
logger: MediaTttReceiverLogger,
viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
- @Main mainExecutor: DelayableExecutor,
+ @Main private val mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
dumpManager: DumpManager,
@@ -285,6 +285,14 @@ constructor(
} else {
rippleController.collapseRipple(rippleView, onAnimationEnd)
animateViewTranslationAndFade(iconContainerView, translationYBy, 0f)
+ mainExecutor.executeDelayed(
+ {
+ if (view.isAttachedToWindow) {
+ onAnimationEnd.run()
+ }
+ },
+ ICON_TRANSLATION_ANIM_DURATION,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 2fda2013d6f5..d33ad8f80021 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,21 +122,20 @@ public class MediaProjectionPermissionActivity extends Activity {
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
- if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
- mPackageName = getLaunchedFromPackage();
- } else {
- mPackageName = getCallingPackage();
- }
- // This activity is launched directly by an app, or system server. System server provides
- // the package name through the intent if so.
- if (mPackageName == null || (
- com.android.systemui.Flags.mediaProjectionRequestAttributionFix()
- && getCallingPackage() == null)) {
+ // The original requester of this activity start
+ mPackageName = getLaunchedFromPackage();
+
+ // This activity is launched directly by using startActivity(),
+ // thus getCallingPackage() will be null.
+ if (getCallingPackage() == null) {
+ // System server provides the package name through the intent if so and is able to get
+ // the result back. Other applications can't.
if (launchingIntent.hasExtra(EXTRA_PACKAGE_REUSING_GRANTED_CONSENT)) {
mPackageName = launchingIntent.getStringExtra(
EXTRA_PACKAGE_REUSING_GRANTED_CONSENT);
} else {
+ // The activity was not launched for result, we abort here
finishAsCancelled();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index d2b1d5449aaf..8f04896fbb45 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -148,7 +148,7 @@ public class NavigationBarControllerImpl implements
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()),
lightBarController, pipOptional, backAnimation.orElse(null),
- taskStackChangeListeners);
+ taskStackChangeListeners, displayTracker);
mIsLargeScreen = isLargeScreen(mContext);
mIsPhone = determineIfPhone(mContext, deviceStateManager);
dumpManager.registerDumpable(this);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 2a3aeae2a550..e9b7534f55e6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -66,6 +66,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
@@ -107,7 +108,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private LightBarTransitionsController mLightBarTransitionsController;
private TaskStackChangeListeners mTaskStackChangeListeners;
private Optional<Pip> mPipOptional;
- private int mDisplayId;
+ private int mDefaultDisplayId;
private int mNavigationIconHints;
private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
new NavBarHelper.NavbarTaskbarStateUpdater() {
@@ -141,7 +142,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onLockTaskModeChanged(int mode) {
mSysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING, mode == LOCK_TASK_MODE_PINNED)
- .commitUpdate(mDisplayId);
+ .commitUpdate(mDefaultDisplayId);
}
};
@@ -159,7 +160,10 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
@Override
public void synchronizeState() {
- checkNavBarModes(mDisplayId);
+ Display[] displays = mDisplayTracker.getAllDisplays();
+ for (Display display : displays) {
+ checkNavBarModes(display.getDisplayId());
+ }
}
@Override
@@ -177,6 +181,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final StatusBarStateController mStatusBarStateController;
+ private DisplayTracker mDisplayTracker;
+
@Inject
public TaskbarDelegate(Context context,
LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
@@ -203,7 +209,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
LightBarController lightBarController,
Optional<Pip> pipOptional,
BackAnimation backAnimation,
- TaskStackChangeListeners taskStackChangeListeners) {
+ TaskStackChangeListeners taskStackChangeListeners,
+ DisplayTracker displayTracker) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
@@ -218,6 +225,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mLightBarTransitionsController = createLightBarTransitionsController();
mTaskStackChangeListeners = taskStackChangeListeners;
mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
+ mDisplayTracker = displayTracker;
}
@Override
@@ -255,7 +263,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
if (mInitialized) {
return;
}
- mDisplayId = displayId;
+ mDefaultDisplayId = displayId;
parseCurrentSysuiState();
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
@@ -315,7 +323,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private void parseCurrentSysuiState() {
NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
- if (state.mWindowStateDisplayId == mDisplayId) {
+ if (state.mWindowStateDisplayId == mDefaultDisplayId) {
mTaskBarWindowState = state.mWindowState;
}
}
@@ -340,7 +348,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
- .commitUpdate(mDisplayId);
+ .commitUpdate(mDefaultDisplayId);
}
boolean isOverviewEnabled() {
@@ -466,7 +474,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void setWindowState(int displayId, int window, int state) {
- if (displayId == mDisplayId
+ if (displayId == mDefaultDisplayId
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mTaskBarWindowState != state) {
mTaskBarWindowState = state;
@@ -498,7 +506,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
nbModeChanged = updateTransitionMode(
transitionMode(mTaskbarTransientShowing, appearance));
}
- if (displayId == mDisplayId) {
+ if (displayId == mDefaultDisplayId) {
mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
mTransitionMode, navbarColorManagedByIme);
}
@@ -510,7 +518,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
- if (displayId != mDisplayId) {
+ if (displayId != mDefaultDisplayId) {
return;
}
if ((types & WindowInsets.Type.navigationBars()) == 0) {
@@ -524,7 +532,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void abortTransient(int displayId, @InsetsType int types) {
- if (displayId != mDisplayId) {
+ if (displayId != mDefaultDisplayId) {
return;
}
if ((types & WindowInsets.Type.navigationBars()) == 0) {
@@ -654,7 +662,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):");
+ pw.println("TaskbarDelegate (mDefaultDisplayId=" + mDefaultDisplayId + "):");
pw.println(" mNavigationIconHints=" + mNavigationIconHints);
pw.println(" mNavigationMode=" + mNavigationMode);
pw.println(" mDisabledFlags=" + mDisabledFlags);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index 2d001508a720..138ac8668a14 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -12,7 +12,6 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.util.LatencyTracker
-import com.android.settingslib.Utils
import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
private const val TAG = "BackPanel"
@@ -156,23 +155,21 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
Configuration.UI_MODE_NIGHT_YES
arrowPaint.color =
- Utils.getColorAttrDefaultColor(
- context,
+ context.getColor(
if (isDeviceInNightTheme) {
- com.android.internal.R.attr.materialColorOnSecondaryContainer
+ com.android.internal.R.color.materialColorOnSecondaryContainer
} else {
- com.android.internal.R.attr.materialColorOnSecondaryFixed
- },
+ com.android.internal.R.color.materialColorOnSecondaryFixed
+ }
)
arrowBackgroundPaint.color =
- Utils.getColorAttrDefaultColor(
- context,
+ context.getColor(
if (isDeviceInNightTheme) {
- com.android.internal.R.attr.materialColorSecondaryContainer
+ com.android.internal.R.color.materialColorSecondaryContainer
} else {
- com.android.internal.R.attr.materialColorSecondaryFixedDim
- },
+ com.android.internal.R.color.materialColorSecondaryFixedDim
+ }
)
}
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 b1719107fae1..037a1b2a97f1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -26,6 +26,7 @@ import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static java.util.stream.Collectors.joining;
@@ -965,11 +966,14 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
return mDesktopModeExcludeRegion.contains(x, y);
}
- private boolean isWithinTouchRegion(int x, int y) {
+ private boolean isWithinTouchRegion(MotionEvent ev) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
+ int x = (int) ev.getX();
+ int y = (int) ev.getY();
final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
- final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y);
+ final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y)
+ && isEdgeResizePermitted(ev);
if (isInsidePip || isInDesktopExcludeRegion
|| mNavBarOverlayExcludedBounds.contains(x, y)) {
return false;
@@ -1098,8 +1102,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
&& isValidTrackpadBackGesture(true /* isTrackpadEvent */);
} else {
mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets
- && isWithinTouchRegion((int) ev.getX(), (int) ev.getY())
- && !isButtonPressFromTrackpad(ev);
+ && isWithinTouchRegion(ev) && !isButtonPressFromTrackpad(ev);
}
if (mAllowGesture) {
mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
index 82420875e0de..43bd6aa37b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -27,6 +27,7 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.power.shared.model.DozeScreenStateModel
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
@@ -64,6 +65,9 @@ interface PowerRepository {
*/
val screenPowerState: StateFlow<ScreenPowerState>
+ /** More granular display states, mainly for use in dozing. */
+ val dozeScreenState: MutableStateFlow<DozeScreenStateModel>
+
/** Wakes up the device. */
fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
@@ -100,6 +104,8 @@ constructor(
dispatcher: BroadcastDispatcher,
) : PowerRepository {
+ override val dozeScreenState = MutableStateFlow(DozeScreenStateModel.UNKNOWN)
+
override val isInteractive: Flow<Boolean> = conflatedCallbackFlow {
fun send() {
trySendWithFailureLogging(manager.isInteractive, TAG)
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 1cf4c23415da..8a3ee1248f57 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.PowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
@@ -33,6 +34,7 @@ import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -72,7 +74,11 @@ constructor(
/** Helper flow in case "isAsleep" reads better than "!isAwake". */
val isAsleep = isAwake.map { !it }
- val screenPowerState = repository.screenPowerState
+ /** The physical on/off state of the display. */
+ val screenPowerState: StateFlow<ScreenPowerState> = repository.screenPowerState
+
+ /** The screen state, related to power and controlled by [DozeScreenState] */
+ val dozeScreenState: StateFlow<DozeScreenStateModel> = repository.dozeScreenState.asStateFlow()
/**
* Notifies the power interactor that a user touch happened.
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt
new file mode 100644
index 000000000000..510b90c071d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.power.shared.model
+
+/** Model device doze screen states. */
+enum class DozeScreenStateModel {
+ /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
+ UNKNOWN,
+ /** Regular doze. Device is asleep and listening for pulse triggers. */
+ OFF,
+ /** Deep doze. Device is asleep and is not listening for pulse triggers. */
+ ON,
+ /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
+ DOZE,
+ /** Pulse has been requested. Device is awake and preparing UI */
+ DOZE_SUSPEND,
+ /** Pulse is showing. Device is awake and showing UI. */
+ VR,
+ /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
+ ON_SUSPEND,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
index b26ae6cdf0bd..f53b6cd29806 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
@@ -40,8 +40,8 @@ import androidx.annotation.WorkerThread
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.animation.ViewHierarchyAnimator
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.maybeForceFullscreen
import java.lang.ref.WeakReference
@@ -347,16 +347,16 @@ class PrivacyDialogV2(
private fun getForegroundColor(active: Boolean) =
Utils.getColorAttrDefaultColor(
context,
- if (active) com.android.internal.R.attr.materialColorOnPrimaryFixed
- else com.android.internal.R.attr.materialColorOnSurface
+ if (active) com.android.internal.R.color.materialColorOnPrimaryFixed
+ else com.android.internal.R.color.materialColorOnSurface,
)
@ColorInt
private fun getBackgroundColor(active: Boolean) =
Utils.getColorAttrDefaultColor(
context,
- if (active) com.android.internal.R.attr.materialColorPrimaryFixed
- else com.android.internal.R.attr.materialColorSurfaceContainerHigh
+ if (active) com.android.internal.R.color.materialColorPrimaryFixed
+ else com.android.internal.R.color.materialColorSurfaceContainerHigh,
)
private fun getMutableDrawable(@DrawableRes resId: Int) = context.getDrawable(resId)!!.mutate()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5c9baa000d0b..91a3120ec770 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -141,6 +141,7 @@ interface FgsManagerController {
class FgsManagerControllerImpl
@Inject
constructor(
+ @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware private val resources: Resources,
@Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
@@ -387,7 +388,7 @@ constructor(
override fun showDialog(expandable: Expandable?) {
synchronized(lock) {
if (dialog == null) {
- val dialog = systemUIDialogFactory.create()
+ val dialog = systemUIDialogFactory.create(context)
dialog.setTitle(R.string.fgs_manager_dialog_title)
dialog.setMessage(R.string.fgs_manager_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index dc188c24e02b..e8ee4dd8ebce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -26,6 +26,7 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.systemui.FontStyles;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
@@ -68,7 +69,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
mBuildText = mView.findViewById(R.id.build);
if (gsfQuickSettings()) {
- mBuildText.setTypeface(Typeface.create("gsf-body-medium", Typeface.NORMAL));
+ mBuildText.setTypeface(Typeface.create(FontStyles.GSF_BODY_MEDIUM, Typeface.NORMAL));
}
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
mEditButton = mView.findViewById(android.R.id.edit);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index ba3357c8b591..7c7f48e0d37b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -19,6 +19,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.res.R;
@@ -80,6 +82,12 @@ public interface QSHost {
void addTile(ComponentName tile);
/**
+ * Click on a tile. Used by external commands
+ * @param tile the component name of the {@link android.service.quicksettings.TileService}
+ */
+ void clickTile(@NonNull ComponentName tile);
+
+ /**
* Adds a custom tile to the set of current tiles.
* @param tile the component name of the {@link android.service.quicksettings.TileService}
* @param end if true, the tile will be added at the end. If false, at the beginning.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 0d464f5a0936..dc3b58247152 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -19,11 +19,13 @@ package com.android.systemui.qs
import android.content.ComponentName
import android.content.Context
import androidx.annotation.GuardedBy
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.external.TileServiceRequestController
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -32,7 +34,6 @@ import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Adapter to determine what real class to use for classes that depend on [QSHost].
@@ -135,4 +136,12 @@ constructor(
override fun indexOf(tileSpec: String): Int {
return specs.indexOf(tileSpec)
}
+
+ override fun clickTile(tile: ComponentName) {
+ if (QsInCompose.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+ val spec = TileSpec.create(tile)
+ interactor.currentTiles.value.firstOrNull { it.spec == spec }?.tile?.click(null)
+ }
}
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 9dc21fb89b16..56fece8ecf5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -33,7 +33,6 @@ import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -47,9 +46,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -78,6 +77,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
@@ -247,51 +247,57 @@ constructor(
private fun Content() {
PlatformTheme(isDarkTheme = true) {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
- AnimatedVisibility(
- visible = viewModel.isQsVisible,
- modifier =
- Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
- // Clipping before translation to match QSContainerImpl.onDraw
- .offset {
- IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
- }
- .thenIf(notificationScrimClippingParams.isEnabled) {
- Modifier.notificationScrimClip {
- notificationScrimClippingParams.params
+ if (viewModel.isQsVisibleAndAnyShadeExpanded) {
+ Box(
+ modifier =
+ Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
+ // Clipping before translation to match QSContainerImpl.onDraw
+ .offset {
+ IntOffset(
+ x = 0,
+ y = viewModel.viewTranslationY.fastRoundToInt(),
+ )
+ }
+ .thenIf(notificationScrimClippingParams.isEnabled) {
+ Modifier.notificationScrimClip {
+ notificationScrimClippingParams.params
+ }
}
+ // Disable touches in the whole composable while the mirror is
+ // showing. While the mirror is showing, an ancestor of the
+ // ComposeView is made alpha 0, but touches are still being captured
+ // by the composables.
+ .gesturesDisabled(viewModel.showingMirror)
+ ) {
+ val isEditing by
+ viewModel.containerViewModel.editModeViewModel.isEditing
+ .collectAsStateWithLifecycle()
+ val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
+ AnimatedContent(
+ targetState = isEditing,
+ transitionSpec = {
+ fadeIn(animationSpecEditMode) togetherWith
+ fadeOut(animationSpecEditMode)
+ },
+ label = "EditModeAnimatedContent",
+ ) { editing ->
+ if (editing) {
+ val qqsPadding = viewModel.qqsHeaderHeight
+ EditMode(
+ viewModel = viewModel.containerViewModel.editModeViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(top = { qqsPadding })
+ .padding(
+ horizontal = {
+ QuickSettingsShade.Dimensions.Padding
+ .roundToPx()
+ }
+ ),
+ )
+ } else {
+ CollapsableQuickSettingsSTL()
}
- // Disable touches in the whole composable while the mirror is showing.
- // While the mirror is showing, an ancestor of the ComposeView is made
- // alpha 0, but touches are still being captured by the composables.
- .gesturesDisabled(viewModel.showingMirror),
- ) {
- val isEditing by
- viewModel.containerViewModel.editModeViewModel.isEditing
- .collectAsStateWithLifecycle()
- val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
- AnimatedContent(
- targetState = isEditing,
- transitionSpec = {
- fadeIn(animationSpecEditMode) togetherWith
- fadeOut(animationSpecEditMode)
- },
- label = "EditModeAnimatedContent",
- ) { editing ->
- if (editing) {
- val qqsPadding = viewModel.qqsHeaderHeight
- EditMode(
- viewModel = viewModel.containerViewModel.editModeViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .padding(top = { qqsPadding })
- .padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding.roundToPx()
- }
- ),
- )
- } else {
- CollapsableQuickSettingsSTL()
}
}
}
@@ -326,9 +332,15 @@ constructor(
}
SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
- scene(QuickSettings) { QuickSettingsElement() }
+ scene(QuickSettings) {
+ LaunchedEffect(Unit) { viewModel.onQSOpen() }
+ QuickSettingsElement()
+ }
- scene(QuickQuickSettings) { QuickQuickSettingsElement() }
+ scene(QuickQuickSettings) {
+ LaunchedEffect(Unit) { viewModel.onQQSOpen() }
+ QuickQuickSettingsElement()
+ }
}
}
@@ -617,7 +629,14 @@ constructor(
val Media =
@Composable {
if (viewModel.qqsMediaVisible) {
- MediaObject(mediaHost = viewModel.qqsMediaHost)
+ MediaObject(
+ // In order to have stable constraints passed to the AndroidView
+ // during expansion (available height changing due to squishiness),
+ // We always allow the media here to be as tall as it wants.
+ // (b/383085298)
+ modifier = Modifier.requiredHeightIn(max = Dp.Infinity),
+ mediaHost = viewModel.qqsMediaHost,
+ )
}
}
@@ -709,12 +728,7 @@ constructor(
GridAnchor()
TileGrid(
viewModel = containerViewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .heightIn(
- max =
- QuickSettingsShade.Dimensions.GridMaxHeight
- ),
+ modifier = Modifier.fillMaxWidth(),
)
}
}
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 02498d69b83d..07ceb6425574 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
@@ -28,6 +28,7 @@ import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.LifecycleCoroutineScope
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.logging.UiEventLogger
import com.android.keyguard.BouncerPanelExpansionCalculator
import com.android.systemui.Dumpable
import com.android.systemui.animation.ShadeInterpolation
@@ -51,6 +52,7 @@ import com.android.systemui.media.dagger.MediaModule.QS_PANEL
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.QSEvent
import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeLog
import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -62,6 +64,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -101,6 +104,7 @@ constructor(
DisableFlagsInteractor: DisableFlagsInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
+ private val shadeInteractor: ShadeInteractor,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
@@ -111,6 +115,7 @@ constructor(
@Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
@Named(QS_PANEL) val qsMediaHost: MediaHost,
@Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
+ private val uiEventLogger: UiEventLogger,
@Assisted private val lifecycleScope: LifecycleCoroutineScope,
) : Dumpable, ExclusiveActivatable() {
@@ -129,6 +134,9 @@ constructor(
var isQsVisible by mutableStateOf(false)
+ val isQsVisibleAndAnyShadeExpanded: Boolean
+ get() = anyShadeExpanded && isQsVisible
+
// This can only be negative if undefined (in which case it will be -1f), else it will be
// in [0, 1]. In some cases, it could be set back to -1f internally to indicate that it's
// different to every value in [0, 1].
@@ -429,6 +437,12 @@ constructor(
),
)
+ private val anyShadeExpanded by
+ hydrator.hydratedStateOf(
+ traceName = "anyShadeExpanded",
+ source = shadeInteractor.isAnyExpanded,
+ )
+
fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) {
if (usingMedia) {
qsMediaHost.currentClipping.set(
@@ -444,6 +458,14 @@ constructor(
falsingInteractor.isFalseTouch(Classifier.QS_SWIPE_NESTED)
}
+ fun onQQSOpen() {
+ uiEventLogger.log(QSEvent.QQS_PANEL_EXPANDED)
+ }
+
+ fun onQSOpen() {
+ uiEventLogger.log(QSEvent.QS_PANEL_EXPANDED)
+ }
+
override suspend fun onActivated(): Nothing {
initMediaHosts() // init regardless of using media (same as current QS).
coroutineScope {
@@ -503,6 +525,8 @@ constructor(
printSection("Quick Settings state") {
println("isQSExpanded", isQsExpanded)
println("isQSVisible", isQsVisible)
+ println("anyShadeExpanded", anyShadeExpanded)
+ println("isQSVisibleAndAnyShadeExpanded", isQsVisibleAndAnyShadeExpanded)
println("isQSEnabled", isQsEnabled)
println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
println("inFirstPage", inFirstPage)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index db778a208b1e..873059ee08db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -47,6 +47,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
+import com.android.systemui.FontStyles;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.qs.QSEditEvent;
@@ -314,7 +315,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
v.setMinimumHeight(calculateHeaderMinHeight(context));
if (gsfQuickSettings()) {
((TextView) v.findViewById(android.R.id.title)).setTypeface(
- Typeface.create("gsf-label-large", Typeface.NORMAL));
+ Typeface.create(FontStyles.GSF_LABEL_LARGE, Typeface.NORMAL));
}
return new Holder(v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 94b8a3ac5a3c..1205c87b2d95 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -16,55 +16,10 @@
package com.android.systemui.qs.dagger;
-import com.android.systemui.media.dagger.MediaModule;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
-import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule;
-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.ui.adapter.QSSceneAdapter;
-import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl;
-
-import java.util.Map;
-
-import dagger.Binds;
import dagger.Module;
-import dagger.multibindings.Multibinds;
/**
- * Module for QS dependencies
+ * Module for QS dependencies for AOSP inclusion
*/
-@Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
- includes = {
- MediaModule.class,
- PanelsModule.class,
- QSFragmentComposeModule.class,
- QSExternalModule.class,
- QSFlagsModule.class,
- QSHostModule.class,
- QSPipelineModule.class,
- QSTilesModule.class,
- }
-)
-public interface QSModule {
-
- /**
- * A map of internal QS tiles. Ensures that this can be injected even if
- * it is empty
- */
- @Multibinds
- Map<String, QSTileImpl<?>> tileMap();
-
- @Binds
- QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
-
- /**
- * Dims the screen
- */
- @Binds
- ReduceBrightColorsController bindReduceBrightColorsController(
- ReduceBrightColorsControllerImpl impl);
-}
+@Module(includes = { QSModuleBase.class})
+public interface QSModule { }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
new file mode 100644
index 000000000000..3fd87689b169
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.dagger
+
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.qs.ReduceBrightColorsController
+import com.android.systemui.qs.ReduceBrightColorsControllerImpl
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
+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.ui.adapter.QSSceneAdapter
+import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.Multibinds
+
+/**
+ * QS Module for shared dependencies between AOSP and variants. Include this module in more
+ * specialized modules (like [QSModule]) and do not include this module directly in SystemUI modules
+ */
+@Module(
+ subcomponents = [QSFragmentComponent::class, QSSceneComponent::class],
+ includes =
+ [
+ MediaModule::class,
+ PanelsModule::class,
+ QSFragmentComposeModule::class,
+ QSExternalModule::class,
+ QSFlagsModule::class,
+ QSHostModule::class,
+ QSPipelineModule::class,
+ QSTilesModule::class,
+ ],
+)
+interface QSModuleBase {
+
+ /** A map of internal QS tiles. Ensures that this can be injected even if it is empty */
+ @Multibinds fun tileMap(): Map<String?, QSTileImpl<*>?>?
+
+ @Binds fun bindsQsSceneAdapter(impl: QSSceneAdapterImpl?): QSSceneAdapter?
+
+ /** Dims the screen */
+ @Binds
+ fun bindReduceBrightColorsController(
+ impl: ReduceBrightColorsControllerImpl?
+ ): ReduceBrightColorsController?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 8ef637545e69..cc872060b827 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,12 +23,12 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -38,6 +38,7 @@ import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
import javax.inject.Named
@@ -54,7 +55,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.isActive
-import com.android.app.tracing.coroutines.launchTraced as launch
private const val TAG = "FooterActionsViewModel"
@@ -113,7 +113,7 @@ class FooterActionsViewModel(
class Factory
@Inject
constructor(
- @ShadeDisplayAware private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val falsingManager: FalsingManager,
private val footerActionsInteractor: FooterActionsInteractor,
private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
@@ -211,7 +211,7 @@ fun FooterActionsViewModel(
false /* if the dismiss should be deferred */
},
null /* cancelAction */,
- true /* afterKeyguardGone */
+ true, /* afterKeyguardGone */
)
}
@@ -269,29 +269,7 @@ fun FooterActionsViewModel(
.distinctUntilChanged()
val userSwitcher =
- footerActionsInteractor.userSwitcherStatus
- .map { userSwitcherStatus ->
- when (userSwitcherStatus) {
- UserSwitcherStatusModel.Disabled -> null
- is UserSwitcherStatusModel.Enabled -> {
- if (userSwitcherStatus.currentUserImage == null) {
- Log.e(
- TAG,
- "Skipped the addition of user switcher button because " +
- "currentUserImage is missing",
- )
- return@map null
- }
-
- userSwitcherButtonViewModel(
- qsThemedContext,
- userSwitcherStatus,
- ::onUserSwitcherClicked
- )
- }
- }
- }
- .distinctUntilChanged()
+ userSwitcherViewModel(qsThemedContext, footerActionsInteractor, ::onUserSwitcherClicked)
val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
val power =
@@ -311,6 +289,36 @@ fun FooterActionsViewModel(
)
}
+fun userSwitcherViewModel(
+ themedContext: Context,
+ footerActionsInteractor: FooterActionsInteractor,
+ onUserSwitcherClicked: (Expandable) -> Unit,
+): Flow<FooterActionsButtonViewModel?> {
+ return footerActionsInteractor.userSwitcherStatus
+ .map { userSwitcherStatus ->
+ when (userSwitcherStatus) {
+ UserSwitcherStatusModel.Disabled -> null
+ is UserSwitcherStatusModel.Enabled -> {
+ if (userSwitcherStatus.currentUserImage == null) {
+ Log.e(
+ TAG,
+ "Skipped the addition of user switcher button because " +
+ "currentUserImage is missing",
+ )
+ return@map null
+ }
+
+ userSwitcherButtonViewModel(
+ themedContext,
+ userSwitcherStatus,
+ onUserSwitcherClicked,
+ )
+ }
+ }
+ }
+ .distinctUntilChanged()
+}
+
fun securityButtonViewModel(
config: SecurityButtonConfig,
onSecurityButtonClicked: (Context, Expandable) -> Unit,
@@ -369,7 +377,7 @@ fun userSwitcherButtonViewModel(
private fun userSwitcherContentDescription(
qsThemedContext: Context,
- currentUser: String?
+ currentUser: String?,
): String? {
return currentUser?.let { user ->
qsThemedContext.getString(R.string.accessibility_quick_settings_user, user)
@@ -384,13 +392,9 @@ fun settingsButtonViewModel(
id = R.id.settings_button_container,
Icon.Resource(
R.drawable.ic_settings,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
),
- iconTint =
- Utils.getColorAttrDefaultColor(
- qsThemedContext,
- R.attr.onShadeInactiveVariant,
- ),
+ iconTint = Utils.getColorAttrDefaultColor(qsThemedContext, R.attr.onShadeInactiveVariant),
backgroundColor = R.attr.shadeInactive,
onSettingsButtonClicked,
)
@@ -404,14 +408,14 @@ fun powerButtonViewModel(
id = R.id.pm_lite,
Icon.Resource(
android.R.drawable.ic_lock_power_off,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
),
iconTint =
Utils.getColorAttrDefaultColor(
qsThemedContext,
- R.attr.onShadeActive,
+ if (DualShade.isEnabled) R.attr.onShadeInactiveVariant else R.attr.onShadeActive,
),
- backgroundColor = R.attr.shadeActive,
+ backgroundColor = if (DualShade.isEnabled) R.attr.shadeInactive else R.attr.shadeActive,
onPowerButtonClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ead38f3f9b52..ef45ae76c20c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -209,7 +209,7 @@ constructor(
// TODO(b/250618218): Remove this method once we know the root cause of b/250618218.
fun logTileBackgroundColorUpdateIfInternetTile(
- tileSpec: String,
+ tileSpec: String?,
state: Int,
disabledByPolicy: Boolean,
color: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 1f55ac777de5..f4bf53cafd19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -21,8 +21,6 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepositoryImpl
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepositoryImpl
import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
import com.android.systemui.qs.panels.domain.interactor.SizedTilesResetInteractor
import com.android.systemui.qs.panels.shared.model.GridLayoutType
@@ -49,9 +47,6 @@ interface PanelsModule {
): DefaultLargeTilesRepository
@Binds
- fun bindGridLayoutTypeRepository(impl: GridLayoutTypeRepositoryImpl): GridLayoutTypeRepository
-
- @Binds
fun bindEditTilesResetInteractor(impl: SizedTilesResetInteractor): EditTilesResetInteractor
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 47c4ffd6a2cc..f17abe888b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -17,28 +17,14 @@
package com.android.systemui.qs.panels.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-interface GridLayoutTypeRepository {
- val layout: StateFlow<GridLayoutType>
-
- fun setLayout(type: GridLayoutType)
-}
+import kotlinx.coroutines.flow.flowOf
@SysUISingleton
-class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
- private val _layout: MutableStateFlow<GridLayoutType> =
- MutableStateFlow(PaginatedGridLayoutType)
- override val layout = _layout.asStateFlow()
+class GridLayoutTypeRepository @Inject constructor() {
+ val defaultLayoutType = flowOf(PaginatedGridLayoutType)
- override fun setLayout(type: GridLayoutType) {
- if (_layout.value != type) {
- _layout.value = type
- }
- }
+ val dualShadeLayoutType = flowOf(InfiniteGridLayoutType)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
index 4af1b2223c4c..e493cbeebae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
@@ -19,14 +19,23 @@ package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
@SysUISingleton
-class GridLayoutTypeInteractor @Inject constructor(private val repo: GridLayoutTypeRepository) {
- val layout: StateFlow<GridLayoutType> = repo.layout
-
- fun setLayoutType(type: GridLayoutType) {
- repo.setLayout(type)
- }
+@OptIn(ExperimentalCoroutinesApi::class)
+class GridLayoutTypeInteractor
+@Inject
+constructor(private val repo: GridLayoutTypeRepository, shadeModeInteractor: ShadeModeInteractor) {
+ val layout: Flow<GridLayoutType> =
+ shadeModeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ is ShadeMode.Dual -> repo.dualShadeLayoutType
+ else -> repo.defaultLayoutType
+ }
+ }
}
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 35faa97db2fe..405ce8a8e5e0 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
@@ -44,19 +44,28 @@ 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 draggedPosition: Offset
val dragInProgress: Boolean
+ val dragType: DragType?
fun isMoving(tileSpec: TileSpec): Boolean
- fun onStarted(cell: SizedTile<EditTileViewModel>)
+ fun onStarted(cell: SizedTile<EditTileViewModel>, dragType: DragType)
- fun onMoved(target: Int, insertAfter: Boolean)
+ fun onTargeting(target: Int, insertAfter: Boolean)
+
+ fun onMoved(offset: Offset)
fun movedOutOfBounds()
fun onDrop()
}
+enum class DragType {
+ Add,
+ Move,
+}
+
/**
* Registers a composable as a [DragAndDropTarget] to receive drop events. Use this outside the tile
* grid to catch out of bounds drops.
@@ -72,6 +81,10 @@ fun Modifier.dragAndDropRemoveZone(
val target =
remember(dragAndDropState) {
object : DragAndDropTarget {
+ override fun onMoved(event: DragAndDropEvent) {
+ dragAndDropState.onMoved(event.toOffset())
+ }
+
override fun onDrop(event: DragAndDropEvent): Boolean {
return dragAndDropState.draggedCell?.let {
onDrop(it.tile.tileSpec)
@@ -117,8 +130,11 @@ fun Modifier.dragAndDropTileList(
}
override fun onMoved(event: DragAndDropEvent) {
+ val offset = event.toOffset()
+ dragAndDropState.onMoved(offset)
+
// Drag offset relative to the list's top left corner
- val relativeDragOffset = event.dragOffsetRelativeTo(contentOffset())
+ val relativeDragOffset = offset - contentOffset()
val targetItem =
gridState.layoutInfo.visibleItemsInfo.firstOrNull { item ->
// Check if the drag is on this item
@@ -126,7 +142,7 @@ fun Modifier.dragAndDropTileList(
}
targetItem?.let {
- dragAndDropState.onMoved(it.index, insertAfter(it, relativeDragOffset))
+ dragAndDropState.onTargeting(it.index, insertAfter(it, relativeDragOffset))
}
}
@@ -147,8 +163,8 @@ fun Modifier.dragAndDropTileList(
)
}
-private fun DragAndDropEvent.dragOffsetRelativeTo(offset: Offset): Offset {
- return toAndroidDragEvent().run { Offset(x, y) } - offset
+private fun DragAndDropEvent.toOffset(): Offset {
+ return toAndroidDragEvent().run { Offset(x, y) }
}
private fun insertAfter(item: LazyGridItemInfo, offset: Offset): Boolean {
@@ -163,6 +179,7 @@ private fun insertAfter(item: LazyGridItemInfo, offset: Offset): Boolean {
fun Modifier.dragAndDropTileSource(
sizedTile: SizedTile<EditTileViewModel>,
dragAndDropState: DragAndDropState,
+ dragType: DragType,
onDragStart: () -> Unit,
): Modifier {
val dragState by rememberUpdatedState(dragAndDropState)
@@ -172,7 +189,7 @@ fun Modifier.dragAndDropTileSource(
detectDragGesturesAfterLongPress(
onDrag = { _, _ -> },
onDragStart = {
- dragState.onStarted(sizedTile)
+ dragState.onStarted(sizedTile, dragType)
onDragStart()
// The tilespec from the ClipData transferred isn't actually needed as we're
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 14abfa2313d8..868855840922 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
@@ -17,10 +17,13 @@
package com.android.systemui.qs.panels.ui.compose
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
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.model.GridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
@@ -48,12 +51,17 @@ class EditTileListState(
private val columns: Int,
private val largeTilesSpan: Int,
) : DragAndDropState {
- private val _draggedCell = mutableStateOf<SizedTile<EditTileViewModel>?>(null)
- override val draggedCell
- get() = _draggedCell.value
+ override var draggedCell by mutableStateOf<SizedTile<EditTileViewModel>?>(null)
+ private set
+
+ override var draggedPosition by mutableStateOf(Offset.Unspecified)
+ private set
+
+ override var dragType by mutableStateOf<DragType?>(null)
+ private set
override val dragInProgress: Boolean
- get() = _draggedCell.value != null
+ get() = draggedCell != null
private val _tiles: SnapshotStateList<GridCell> =
tiles.toGridCells(columns).toMutableStateList()
@@ -83,18 +91,19 @@ class EditTileListState(
}
override fun isMoving(tileSpec: TileSpec): Boolean {
- return _draggedCell.value?.let { it.tile.tileSpec == tileSpec } ?: false
+ return draggedCell?.let { it.tile.tileSpec == tileSpec } ?: false
}
- override fun onStarted(cell: SizedTile<EditTileViewModel>) {
- _draggedCell.value = cell
+ 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 onMoved(target: Int, insertAfter: Boolean) {
- val draggedTile = _draggedCell.value ?: return
+ override fun onTargeting(target: Int, insertAfter: Boolean) {
+ val draggedTile = draggedCell ?: return
val fromIndex = indexOf(draggedTile.tile.tileSpec)
if (fromIndex == target) {
@@ -115,16 +124,26 @@ class EditTileListState(
regenerateGrid()
}
+ override fun onMoved(offset: Offset) {
+ draggedPosition = offset
+ }
+
override fun movedOutOfBounds() {
- val draggedTile = _draggedCell.value ?: return
+ val draggedTile = draggedCell ?: return
_tiles.removeIf { cell ->
cell is TileGridCell && cell.tile.tileSpec == draggedTile.tile.tileSpec
}
+ draggedPosition = Offset.Unspecified
+
+ // Regenerate spacers without the dragged tile
+ regenerateGrid()
}
override fun onDrop() {
- _draggedCell.value = null
+ draggedCell = null
+ draggedPosition = Offset.Unspecified
+ dragType = null
// Remove the spacers
regenerateGrid()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index b6dbf4db57a4..39408d3dee72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -49,9 +49,10 @@ import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight
import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing
-import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
+import com.android.systemui.qs.panels.ui.compose.toolbar.EditModeButton
import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import com.android.systemui.qs.ui.compose.borderOnFocus
import javax.inject.Inject
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 c6141a1a7cc2..d975f104d538 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
@@ -20,14 +20,19 @@ package com.android.systemui.qs.panels.ui.compose.infinitegrid
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollFactory
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.clipScrollableContainer
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
@@ -42,6 +47,7 @@ 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.requiredHeightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
@@ -68,6 +74,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.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -79,6 +86,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
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.layout.MeasureScope
@@ -110,6 +118,7 @@ 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.BounceableInfo
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.bounceableInfo
import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone
@@ -119,6 +128,9 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_DISTANCE
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer
@@ -138,9 +150,10 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.res.R
-import kotlin.math.max
+import kotlin.math.abs
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
object TileType
@@ -148,8 +161,9 @@ object TileType
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
+
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Black),
+ colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
title = { Text(text = stringResource(id = R.string.qs_edit)) },
navigationIcon = {
IconButton(onClick = onStopEditing) {
@@ -200,8 +214,12 @@ fun DefaultEditTileGrid(
) { innerPadding ->
CompositionLocalProvider(LocalOverscrollFactory provides null) {
val scrollState = rememberScrollState()
- LaunchedEffect(listState.dragInProgress) {
- if (listState.dragInProgress) {
+
+ AutoScrollGrid(listState, scrollState, innerPadding)
+
+ LaunchedEffect(listState.dragType) {
+ // Only scroll to the top when adding a new tile, not when reordering existing ones
+ if (listState.dragInProgress && listState.dragType == DragType.Add) {
scrollState.animateScrollTo(0)
}
}
@@ -209,12 +227,20 @@ fun DefaultEditTileGrid(
Column(
verticalArrangement =
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize().verticalScroll(scrollState).padding(innerPadding),
+ modifier =
+ modifier
+ .fillMaxSize()
+ // Apply top padding before the scroll so the scrollable doesn't show under
+ // the
+ // top bar
+ .padding(top = innerPadding.calculateTopPadding())
+ .clipScrollableContainer(Orientation.Vertical)
+ .verticalScroll(scrollState),
) {
AnimatedContent(
targetState = listState.dragInProgress,
modifier = Modifier.wrapContentSize(),
- label = "",
+ label = "QSEditHeader",
) { dragIsInProgress ->
EditGridHeader(Modifier.dragAndDropRemoveZone(listState, onRemoveTile)) {
if (dragIsInProgress) {
@@ -234,34 +260,84 @@ fun DefaultEditTileGrid(
onSetTiles,
)
- // Hide available tiles when dragging
- AnimatedVisibility(
- visible = !listState.dragInProgress,
- enter = fadeIn(),
- exit = fadeOut(),
+ // Sets a minimum height to be used when available tiles are hidden
+ Box(
+ Modifier.fillMaxWidth()
+ .requiredHeightIn(AvailableTilesGridMinHeight)
+ .animateContentSize()
+ .dragAndDropRemoveZone(listState, onRemoveTile)
) {
- Column(
- verticalArrangement =
- spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize(),
+ // Using the fully qualified name here as a workaround for AnimatedVisibility
+ // not being available from a Box
+ androidx.compose.animation.AnimatedVisibility(
+ visible = !listState.dragInProgress,
+ enter = fadeIn(),
+ exit = fadeOut(),
) {
- EditGridHeader {
- Text(text = stringResource(id = R.string.drag_to_add_tiles))
- }
+ // Hide available tiles when dragging
+ Column(
+ verticalArrangement =
+ spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
+ modifier = modifier.fillMaxSize(),
+ ) {
+ EditGridHeader {
+ Text(text = stringResource(id = R.string.drag_to_add_tiles))
+ }
- AvailableTileGrid(otherTiles, selectionState, columns, listState)
+ AvailableTileGrid(otherTiles, selectionState, columns, listState)
+ }
}
}
+ }
+ }
+ }
+}
- // Drop zone to remove tiles dragged out of the tile grid
- Spacer(
- modifier =
- Modifier.fillMaxWidth()
- .weight(1f)
- .dragAndDropRemoveZone(listState, onRemoveTile)
- )
+@OptIn(ExperimentalCoroutinesApi::class)
+@Composable
+private fun AutoScrollGrid(
+ listState: EditTileListState,
+ scrollState: ScrollState,
+ padding: PaddingValues,
+) {
+ val density = LocalDensity.current
+ val (top, bottom) =
+ remember(density) {
+ with(density) {
+ padding.calculateTopPadding().roundToPx() to
+ padding.calculateBottomPadding().roundToPx()
+ }
+ }
+ val scrollTarget by
+ remember(listState, scrollState, top, bottom) {
+ derivedStateOf {
+ val position = listState.draggedPosition
+ if (position.isSpecified) {
+ // Return the scroll target needed based on the position of the drag movement,
+ // or null if we don't need to scroll
+ val y = position.y.roundToInt()
+ when {
+ y < AUTO_SCROLL_DISTANCE + top -> 0
+ y > scrollState.viewportSize - bottom - AUTO_SCROLL_DISTANCE ->
+ scrollState.maxValue
+ else -> null
+ }
+ } else {
+ null
+ }
}
}
+ LaunchedEffect(scrollTarget) {
+ scrollTarget?.let {
+ // Change the duration of the animation based on the distance to maintain the
+ // same scrolling speed
+ val distance = abs(it - scrollState.value)
+ scrollState.animateScrollTo(
+ it,
+ animationSpec =
+ tween(durationMillis = distance * AUTO_SCROLL_SPEED, easing = LinearEasing),
+ )
+ }
}
}
@@ -414,7 +490,7 @@ private fun AvailableTileGrid(
}
fun gridHeight(rows: Int, tileHeight: Dp, tilePadding: Dp, gridPadding: Dp): Dp {
- return ((tileHeight + tilePadding) * rows) - tilePadding + gridPadding * 2
+ return ((tileHeight + tilePadding) * rows) + gridPadding * 2
}
private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
@@ -587,6 +663,7 @@ private fun TileGridCell(
.dragAndDropTileSource(
SizedTileImpl(cell.tile, cell.width),
dragAndDropState,
+ DragType.Move,
selectionState::unSelect,
)
.tileBackground(colors.background)
@@ -622,7 +699,11 @@ private fun AvailableTileGridCell(
onClick(onClickActionName) { false }
this.stateDescription = stateDescription
}
- .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
+ .dragAndDropTileSource(
+ SizedTileImpl(cell.tile, cell.width),
+ dragAndDropState,
+ DragType.Add,
+ ) {
selectionState.unSelect()
}
.tileBackground(colors.background)
@@ -730,7 +811,10 @@ private fun Modifier.tileBackground(color: Color): Modifier {
private object EditModeTileDefaults {
const val PLACEHOLDER_ALPHA = .3f
+ const val AUTO_SCROLL_DISTANCE = 100
+ const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
val CurrentTilesGridPadding = 8.dp
+ val AvailableTilesGridMinHeight = 200.dp
@Composable
fun editTileColors(): TileColors =
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 c798e5bb6dc7..13b331163d44 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
@@ -126,7 +126,7 @@ fun Tile(
modifier: Modifier = Modifier,
detailsViewModel: DetailsViewModel?,
) {
- val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
+ val state: QSTile.State by tile.state.collectAsStateWithLifecycle(tile.currentState)
val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
index c2764f9f338b..85db95203b45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.compose
+package com.android.systemui.qs.panels.ui.compose.toolbar
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
new file mode 100644
index 000000000000..37fa9e799521
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.panels.ui.compose.toolbar
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.footer.ui.compose.IconButton
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
+
+@Composable
+fun Toolbar(toolbarViewModelFactory: ToolbarViewModel.Factory, modifier: Modifier = Modifier) {
+ val viewModel = rememberViewModel("Toolbar") { toolbarViewModelFactory.create() }
+
+ Row(
+ modifier = modifier.fillMaxWidth().requiredHeight(48.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ viewModel.userSwitcherViewModel?.let {
+ IconButton(it, Modifier.sysuiResTag("multi_user_switch"))
+ }
+
+ EditModeButton(viewModel.editModeButtonViewModelFactory)
+
+ IconButton(
+ viewModel.settingsButtonViewModel,
+ Modifier.sysuiResTag("settings_button_container"),
+ )
+
+ Spacer(modifier = Modifier.weight(1f))
+ IconButton(viewModel.powerButtonViewModel, Modifier.sysuiResTag("pm_lite"))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index faab6960a99c..f7ed1adecb34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -18,9 +18,14 @@ package com.android.systemui.qs.panels.ui.viewmodel
import android.content.Context
import androidx.compose.ui.util.fastMap
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListUpdateCallback
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.QSEditEvent
import com.android.systemui.qs.panels.domain.interactor.EditTilesListInteractor
import com.android.systemui.qs.panels.domain.interactor.GridLayoutTypeInteractor
import com.android.systemui.qs.panels.domain.interactor.TilesAvailabilityInteractor
@@ -30,10 +35,12 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.metricSpec
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import javax.inject.Named
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -45,6 +52,7 @@ import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
@SysUISingleton
@OptIn(ExperimentalCoroutinesApi::class)
@@ -55,10 +63,12 @@ constructor(
private val currentTilesInteractor: CurrentTilesInteractor,
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
+ private val uiEventLogger: UiEventLogger,
@ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
- @ShadeDisplayAware private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
gridLayoutTypeInteractor: GridLayoutTypeInteractor,
gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
) {
@@ -149,11 +159,17 @@ constructor(
/** @see isEditing */
fun startEditing() {
+ if (!isEditing.value) {
+ uiEventLogger.log(QSEditEvent.QS_EDIT_OPEN)
+ }
_isEditing.value = true
}
/** @see isEditing */
fun stopEditing() {
+ if (isEditing.value) {
+ uiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED)
+ }
_isEditing.value = false
}
@@ -164,6 +180,7 @@ constructor(
fun addTile(tileSpec: TileSpec, position: Int = POSITION_AT_END) {
val specs = currentTilesInteractor.currentTilesSpecs.toMutableList()
val currentPosition = specs.indexOf(tileSpec)
+ val moved = currentPosition != -1
if (currentPosition != -1) {
// No operation needed if the element is already in the list at the right position
@@ -179,6 +196,12 @@ constructor(
} else {
specs.add(tileSpec)
}
+ uiEventLogger.logWithPosition(
+ if (moved) QSEditEvent.QS_EDIT_MOVE else QSEditEvent.QS_EDIT_ADD,
+ /* uid= */ 0,
+ /* packageName= */ tileSpec.metricSpec,
+ if (moved && position == POSITION_AT_END) specs.size - 1 else position,
+ )
// Setting the new tiles as one operation to avoid UI jank with tiles disappearing and
// reappearing
@@ -187,10 +210,80 @@ constructor(
/** Immediately removes [tileSpec] from the current tiles. */
fun removeTile(tileSpec: TileSpec) {
+ uiEventLogger.log(
+ QSEditEvent.QS_EDIT_REMOVE,
+ /* uid= */ 0,
+ /* packageName= */ tileSpec.metricSpec,
+ )
currentTilesInteractor.removeTiles(listOf(tileSpec))
}
fun setTiles(tileSpecs: List<TileSpec>) {
+ val currentTiles = currentTilesInteractor.currentTilesSpecs
currentTilesInteractor.setTiles(tileSpecs)
+ applicationScope.launch(bgDispatcher) {
+ calculateDiffsAndEmitUiEvents(currentTiles, tileSpecs)
+ }
+ }
+
+ private fun calculateDiffsAndEmitUiEvents(
+ currentTiles: List<TileSpec>,
+ newTiles: List<TileSpec>,
+ ) {
+ val listDiff = DiffUtil.calculateDiff(DiffCallback(currentTiles, newTiles))
+ listDiff.dispatchUpdatesTo(
+ object : ListUpdateCallback {
+ override fun onInserted(position: Int, count: Int) {
+ newTiles.getOrNull(position)?.let {
+ uiEventLogger.logWithPosition(
+ QSEditEvent.QS_EDIT_ADD,
+ /* uid= */ 0,
+ /* packageName= */ it.metricSpec,
+ position,
+ )
+ }
+ }
+
+ override fun onRemoved(position: Int, count: Int) {
+ currentTiles.getOrNull(position)?.let {
+ uiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, it.metricSpec)
+ }
+ }
+
+ override fun onMoved(fromPosition: Int, toPosition: Int) {
+ currentTiles.getOrNull(fromPosition)?.let {
+ uiEventLogger.logWithPosition(
+ QSEditEvent.QS_EDIT_MOVE,
+ /* uid= */ 0,
+ /* packageName= */ it.metricSpec,
+ toPosition,
+ )
+ }
+ }
+
+ override fun onChanged(position: Int, count: Int, payload: Any?) {}
+ }
+ )
+ }
+}
+
+private class DiffCallback(
+ private val currentList: List<TileSpec>,
+ private val newList: List<TileSpec>,
+) : DiffUtil.Callback() {
+ override fun getOldListSize(): Int {
+ return currentList.size
+ }
+
+ override fun getNewListSize(): Int {
+ return newList.size
+ }
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return currentList[oldItemPosition] == newList[newItemPosition]
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return areItemsTheSame(oldItemPosition, newItemPosition)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index 4a18872ad6f6..3fcb2ab37b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index 44dd801a8b9f..9462321ad9fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onStart
@Immutable
@@ -37,6 +38,7 @@ class TileViewModel(private val tile: QSTile, val spec: TileSpec) {
awaitClose { tile.removeCallback(callback) }
}
.onStart { emit(tile.state) }
+ .filterNotNull()
.distinctUntilChanged()
val currentState: QSTile.State
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt
index b033473a91e5..f60621882ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.qs.panels.ui.viewmodel.toolbar
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
new file mode 100644
index 000000000000..0fde855f576f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.panels.ui.viewmodel.toolbar
+
+import android.content.Context
+import android.view.ContextThemeWrapper
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import com.android.systemui.animation.Expandable
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
+import com.android.systemui.classifier.domain.interactor.runIfNotFalseTap
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.powerButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.settingsButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.userSwitcherViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import javax.inject.Provider
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+class ToolbarViewModel
+@AssistedInject
+constructor(
+ val editModeButtonViewModelFactory: EditModeButtonViewModel.Factory,
+ private val footerActionsInteractor: FooterActionsInteractor,
+ private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
+ private val falsingInteractor: FalsingInteractor,
+ @ShadeDisplayAware appContext: Context,
+) : ExclusiveActivatable() {
+ private val qsThemedContext =
+ ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+ private val hydrator = Hydrator("ToolbarViewModel.hydrator")
+
+ val powerButtonViewModel = powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+
+ val settingsButtonViewModel =
+ settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
+
+ val userSwitcherViewModel: FooterActionsButtonViewModel? by
+ hydrator.hydratedStateOf(
+ traceName = "userSwitcherViewModel",
+ initialValue = null,
+ source =
+ userSwitcherViewModel(
+ qsThemedContext,
+ footerActionsInteractor,
+ ::onUserSwitcherClicked,
+ ),
+ )
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch {
+ try {
+ globalActionsDialogLite = globalActionsDialogLiteProvider.get()
+ awaitCancellation()
+ } finally {
+ globalActionsDialogLite?.destroy()
+ }
+ }
+ launch { hydrator.activate() }
+ awaitCancellation()
+ }
+ }
+
+ private var globalActionsDialogLite: GlobalActionsDialogLite? by mutableStateOf(null)
+
+ private fun onPowerButtonClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap {
+ globalActionsDialogLite?.let {
+ footerActionsInteractor.showPowerMenuDialog(it, expandable)
+ }
+ }
+ }
+
+ private fun onUserSwitcherClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap { footerActionsInteractor.showUserSwitcher(expandable) }
+ }
+
+ private fun onSettingsButtonClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap { footerActionsInteractor.showSettings(expandable) }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): ToolbarViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 2e52845ceb80..16c27223a471 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -34,10 +34,7 @@ sealed class TileSpec private constructor(open val spec: String) {
data object Invalid : TileSpec("")
/** Container for the spec of a tile provided by SystemUI. */
- data class PlatformTileSpec
- internal constructor(
- override val spec: String,
- ) : TileSpec(spec) {
+ data class PlatformTileSpec internal constructor(override val spec: String) : TileSpec(spec) {
override fun toString(): String {
return "P($spec)"
}
@@ -49,10 +46,8 @@ sealed class TileSpec private constructor(open val spec: String) {
* [componentName] indicates the associated `TileService`.
*/
data class CustomTileSpec
- internal constructor(
- override val spec: String,
- val componentName: ComponentName,
- ) : TileSpec(spec) {
+ internal constructor(override val spec: String, val componentName: ComponentName) :
+ TileSpec(spec) {
override fun toString(): String {
return "C(${componentName.flattenToShortString()})"
}
@@ -92,3 +87,11 @@ sealed class TileSpec private constructor(open val spec: String) {
}
}
}
+
+val TileSpec.metricSpec
+ get() =
+ when (this) {
+ is TileSpec.Invalid -> ""
+ is TileSpec.PlatformTileSpec -> spec
+ is TileSpec.CustomTileSpec -> componentName.packageName
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 464eeda6a0a8..1d1e9911884c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -369,6 +369,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mHandler.sendEmptyMessage(H.INITIALIZE);
}
+ @androidx.annotation.NonNull
public TState getState() {
return mState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index b7ebce247ec9..d401b6ecbfd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -60,6 +60,7 @@ import com.android.app.tracing.traceSection
import com.android.settingslib.Utils
import com.android.systemui.Flags
import com.android.systemui.FontSizeUtils
+import com.android.systemui.FontStyles
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
@@ -312,9 +313,11 @@ constructor(
if (Flags.gsfQuickSettings()) {
label.apply {
- typeface = Typeface.create("gsf-title-small-emphasized", Typeface.NORMAL)
+ typeface = Typeface.create(FontStyles.GSF_TITLE_SMALL_EMPHASIZED, Typeface.NORMAL)
+ }
+ secondaryLabel.apply {
+ typeface = Typeface.create(FontStyles.GSF_LABEL_MEDIUM, Typeface.NORMAL)
}
- secondaryLabel.apply { typeface = Typeface.create("gsf-label-medium", Typeface.NORMAL) }
}
addView(labelContainer)
@@ -776,11 +779,15 @@ constructor(
lastIconTint = icon.getColor(state)
// Long-press effects
- longPressEffect?.qsTile?.state?.handlesLongClick = state.handlesLongClick
- if (
- state.handlesLongClick &&
- longPressEffect?.initializeEffect(longPressEffectDuration) == true
- ) {
+ updateLongPressEffect(state.handlesLongClick)
+ }
+
+ private fun updateLongPressEffect(handlesLongClick: Boolean) {
+ // The long press effect in the tile can't be updated if it is still running
+ if (longPressEffect?.state != QSLongPressEffect.State.IDLE) return
+
+ longPressEffect.qsTile?.state?.handlesLongClick = handlesLongClick
+ if (handlesLongClick && longPressEffect.initializeEffect(longPressEffectDuration)) {
showRippleEffect = false
longPressEffect.qsTile?.state?.state = lastState // Store the tile's state
longPressEffect.resetState()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index deeef550b33f..42a0cb1004f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -102,7 +102,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
// Show a dialog to confirm first. Dialogs shown by the DialogTransitionAnimator must be
// created and shown on the main thread, so we post it to the UI handler.
mUiHandler.post(() -> {
- SystemUIDialog dialog = mSystemUIDialogFactory.create();
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(mContext);
dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
dialog.setMessage(com.android.internal.R.string.data_saver_description);
dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
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 9c6345666403..0051bf5de7f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -109,6 +109,11 @@ constructor(
userActionInteractor.handleClick(expandable)
}
+ override fun handleSecondaryClick(expandable: Expandable?) = runBlocking {
+ val model = dataInteractor.getCurrentTileModel()
+ userActionInteractor.handleToggleClick(model)
+ }
+
override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
@VisibleForTesting
@@ -125,6 +130,7 @@ constructor(
secondaryLabel = tileState.secondaryLabel
contentDescription = tileState.contentDescription
expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
+ handlesSecondaryClick = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 4fb96e72d8df..ec8d30b01eab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -24,6 +24,7 @@ import android.os.Looper;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Button;
import android.widget.Switch;
import androidx.annotation.Nullable;
@@ -144,8 +145,10 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
// Show expand icon when clicking will open a dialog
state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
+ state.expandedAccessibilityClassName = Button.class.getName();
if (isRecording) {
state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_stop);
+ state.expandedAccessibilityClassName = Switch.class.getName();
} else if (isStarting) {
int countdown =
(int) ScreenRecordModel.Starting.Companion.toCountdownSeconds(
@@ -157,7 +160,6 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
? state.label
: TextUtils.concat(state.label, ", ", state.secondaryLabel);
- state.expandedAccessibilityClassName = Switch.class.getName();
}
@Override
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/interactor/QSTileUserActionInteractor.kt
index 17b78ebf106c..e8c4274474e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.base.interactor
import android.annotation.WorkerThread
+import com.android.systemui.plugins.qs.TileDetailsViewModel
interface QSTileUserActionInteractor<DATA_TYPE> {
/**
@@ -27,4 +28,17 @@ interface QSTileUserActionInteractor<DATA_TYPE> {
* It's safe to run long running computations inside this function.
*/
@WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>)
+
+ /**
+ * Provides the [TileDetailsViewModel] for constructing the corresponding details view.
+ *
+ * This property is defined here to reuse the business logic. For example, reusing the user
+ * long-click as the go-to-settings callback in the details view.
+ * Subclasses can override this property to provide a specific [TileDetailsViewModel]
+ * implementation.
+ *
+ * @return The [TileDetailsViewModel] instance, or null if not implemented.
+ */
+ val detailsViewModel: TileDetailsViewModel?
+ get() = null
}
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/viewmodel/QSTileCoroutineScopeFactory.kt
index dde36289f139..5f476ea7e274 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
@@ -17,18 +17,17 @@
package com.android.systemui.qs.tiles.base.viewmodel
import com.android.systemui.coroutines.newTracingContext
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
/** Creates a [CoroutineScope] for the [QSTileViewModelImpl]. */
class QSTileCoroutineScopeFactory
@Inject
-constructor(@Application private val applicationScope: CoroutineScope) {
+constructor(@Background private val bgDispatcher: CoroutineDispatcher) {
fun create(): CoroutineScope =
- CoroutineScope(
- applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope")
- )
+ CoroutineScope(bgDispatcher + SupervisorJob() + newTracingContext("QSTileScope"))
}
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/viewmodel/QSTileViewModelImpl.kt
index aeb6cef162b5..224fa104168d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -20,6 +20,7 @@ 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
@@ -115,6 +116,9 @@ class QSTileViewModelImpl<DATA_TYPE>(
.flowOn(backgroundDispatcher)
.stateIn(tileScope, SharingStarted.WhileSubscribed(), true)
+ override val detailsViewModel: TileDetailsViewModel?
+ get() = userActionInteractor().detailsViewModel
+
override fun forceUpdate() {
tileScope.launch(context = backgroundDispatcher) { forceUpdates.emit(Unit) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 244f024625db..dbe1ae90b3f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -149,7 +149,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
static final int MAX_WIFI_ENTRY_COUNT = 3;
@@ -195,8 +195,11 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private boolean mHasWifiEntries;
private WifiStateWorker mWifiStateWorker;
private boolean mHasActiveSubIdOnDds;
+ private boolean mIsMobileDataEnabled = false;
@VisibleForTesting
+ Map<Integer, ServiceState> mSubIdServiceState = new HashMap<>();
+ @VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@VisibleForTesting
static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
@@ -453,7 +456,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
- if (mCanConfigWifi && !isMobileDataEnabled()) {
+ if (mCanConfigWifi && !mIsMobileDataEnabled) {
if (DEBUG) {
Log.d(TAG, "Mobile data off");
}
@@ -551,7 +554,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
numLevels += 1;
}
return getSignalStrengthIcon(subId, mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
- !isMobileDataEnabled());
+ !mIsMobileDataEnabled);
}
Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
@@ -681,6 +684,12 @@ public class InternetDialogController implements AccessPointController.AccessPoi
// sets the non-DDS to be not found to hide its visual
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
+ int activeDataSubId = SubscriptionManager.getActiveDataSubscriptionId();
+ if (activeDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ || mDefaultDataSubId == activeDataSubId) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
SubscriptionManager.getActiveDataSubscriptionId());
if (subInfo != null && subInfo.getSubscriptionId() != mDefaultDataSubId
@@ -740,7 +749,6 @@ public class InternetDialogController implements AccessPointController.AccessPoi
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
-
String summary = networkTypeDescription;
boolean isForDds = subId == mDefaultDataSubId;
int activeSubId = getActiveAutoSwitchNonDdsSubId();
@@ -753,8 +761,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
context.getString(
isForDds // if nonDds is active, explains Dds status as poor connection
? (isOnNonDds ? R.string.mobile_data_poor_connection
- : R.string.mobile_data_connection_active)
- : R.string.mobile_data_temp_connection_active),
+ : R.string.mobile_data_connection_active)
+ : R.string.mobile_data_temp_connection_active),
networkTypeDescription);
} else if (!isDataStateInService(subId)) {
summary = context.getString(R.string.mobile_data_no_connection);
@@ -963,10 +971,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
* Return {@code true} if mobile data is enabled
*/
boolean isMobileDataEnabled() {
- if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
- return false;
- }
- return true;
+ return mIsMobileDataEnabled;
}
/**
@@ -1019,8 +1024,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
boolean isDataStateInService(int subId) {
- TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
- final ServiceState serviceState = tm.getServiceState();
+ final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
+ new ServiceState());
NetworkRegistrationInfo regInfo =
(serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
@@ -1036,8 +1041,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return false;
}
- TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
- final ServiceState serviceState = tm.getServiceState();
+ final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
+ new ServiceState());
return serviceState != null
&& serviceState.getState() == serviceState.STATE_IN_SERVICE;
}
@@ -1056,6 +1061,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
+ Log.d(TAG, "getActiveNetwork is null.");
return false;
}
final NetworkCapabilities networkCapabilities =
@@ -1183,14 +1189,16 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
private class InternetTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataEnabledListener,
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.UserMobileDataStateListener,
- TelephonyCallback.CarrierNetworkListener{
+ TelephonyCallback.CarrierNetworkListener {
private final int mSubId;
+
private InternetTelephonyCallback(int subId) {
mSubId = subId;
}
@@ -1200,6 +1208,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
if (mCallback != null) {
mCallback.onServiceStateChanged(serviceState);
}
+ mSubIdServiceState.put(mSubId, serviceState);
}
@Override
@@ -1238,6 +1247,13 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mCallback.onCarrierNetworkChange(active);
}
}
+
+ @Override
+ public void onDataEnabledChanged(boolean b, int i) {
+ if (mSubId == mDefaultDataSubId) {
+ mIsMobileDataEnabled = b;
+ }
+ }
}
private class InternetOnSubscriptionChangedListener
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 0ab533bb9838..70c2a2a0d55a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -104,6 +104,7 @@ public class InternetDialogDelegate implements
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final Context mContext;
private final boolean mAboveStatusBar;
private final SystemUIDialog.Factory mSystemUIDialogFactory;
@@ -204,6 +205,7 @@ public class InternetDialogDelegate implements
@Background Executor executor,
KeyguardStateController keyguardStateController,
SystemUIDialog.Factory systemUIDialogFactory) {
+ mContext = context;
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
if (DEBUG) {
@@ -228,7 +230,7 @@ public class InternetDialogDelegate implements
@Override
public SystemUIDialog createDialog() {
- SystemUIDialog dialog = mSystemUIDialogFactory.create(this);
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(this, mContext);
if (!mAboveStatusBar) {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
@@ -416,9 +418,10 @@ public class InternetDialogDelegate implements
internetContent.mHasEthernet = mInternetDialogController.hasEthernet();
internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
internetContent.mHasActiveSubIdOnDds = mInternetDialogController.hasActiveSubIdOnDds();
- internetContent.mIsMobileDataEnabled = mInternetDialogController.isMobileDataEnabled();
internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
internetContent.mIsWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
+ internetContent.mActiveAutoSwitchNonDdsSubId =
+ mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
return internetContent;
}
@@ -433,7 +436,11 @@ public class InternetDialogDelegate implements
private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
- int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ int autoSwitchNonDdsSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (mDataInternetContent.getValue() != null) {
+ autoSwitchNonDdsSubId =
+ mDataInternetContent.getValue().mActiveAutoSwitchNonDdsSubId;
+ }
if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId);
}
@@ -524,7 +531,7 @@ public class InternetDialogDelegate implements
}
} else {
mMobileNetworkLayout.setVisibility(View.VISIBLE);
- mMobileDataToggle.setChecked(internetContent.mIsMobileDataEnabled);
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId));
String summary = getMobileNetworkSummary(mDefaultDataSubId);
if (!TextUtils.isEmpty(summary)) {
@@ -549,9 +556,9 @@ public class InternetDialogDelegate implements
? R.color.connected_network_primary_color
: R.color.disconnected_network_primary_color;
mMobileToggleDivider.setBackgroundColor(dialog.getContext().getColor(primaryColor));
-
// Display the info for the non-DDS if it's actively being used
- int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ int autoSwitchNonDdsSubId = internetContent.mActiveAutoSwitchNonDdsSubId;
+
int nonDdsVisibility = autoSwitchNonDdsSubId
!= SubscriptionManager.INVALID_SUBSCRIPTION_ID ? View.VISIBLE : View.GONE;
@@ -983,8 +990,8 @@ public class InternetDialogDelegate implements
boolean mIsCarrierNetworkActive = false;
boolean mIsWifiEnabled = false;
boolean mHasActiveSubIdOnDds = false;
- boolean mIsMobileDataEnabled = false;
boolean mIsDeviceLocked = false;
boolean mIsWifiScanEnabled = false;
+ int mActiveAutoSwitchNonDdsSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
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 a963b2875154..c4f9515b819f 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
@@ -18,10 +18,13 @@ package com.android.systemui.qs.tiles.impl.internet.domain.interactor
import android.content.Intent
import android.provider.Settings
+import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.qs.TileDetailsViewModel
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.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
@@ -61,11 +64,18 @@ constructor(
wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
}
is QSTileUserAction.LongClick -> {
- qsTileIntentUserActionHandler.handle(
- action.expandable,
- Intent(Settings.ACTION_WIFI_SETTINGS)
- )
+ handleLongClick(action.expandable)
}
}
}
+
+ override val detailsViewModel: TileDetailsViewModel =
+ InternetDetailsViewModel { handleLongClick(null) }
+
+ private fun handleLongClick(expandable:Expandable?){
+ qsTileIntentUserActionHandler.handle(
+ expandable,
+ Intent(Settings.ACTION_WIFI_SETTINGS)
+ )
+ }
}
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 eb8b23c2505a..594394f68d48 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
@@ -18,13 +18,16 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import android.content.Intent
import android.provider.Settings
+import android.util.Log
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
+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.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 javax.inject.Inject
@@ -35,16 +38,19 @@ constructor(
private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
// TODO(b/353896370): The domain layer should not have to depend on the UI layer.
private val dialogDelegate: ModesDialogDelegate,
+ private val zenModeInteractor: ZenModeInteractor,
) : QSTileUserActionInteractor<ModesTileModel> {
val longClickIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
override suspend fun handleInput(input: QSTileInput<ModesTileModel>) {
with(input) {
when (action) {
- is QSTileUserAction.Click,
- is QSTileUserAction.ToggleClick -> {
+ is QSTileUserAction.Click -> {
handleClick(action.expandable)
}
+ is QSTileUserAction.ToggleClick -> {
+ handleToggleClick(input.data)
+ }
is QSTileUserAction.LongClick -> {
qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
}
@@ -56,4 +62,29 @@ constructor(
// Show a dialog with the list of modes to configure.
dialogDelegate.showDialog(expandable)
}
+
+ fun handleToggleClick(modesTileModel: ModesTileModel) {
+ if (QSComposeFragment.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+
+ // If no modes are on, turn on DND since it's the highest-priority mode. Otherwise, turn
+ // them all off.
+ // We want this toggle to work as a shortcut to DND in most cases, but it should still
+ // correctly toggle the tile state to "off" as the user would expect when more modes are on.
+ if (modesTileModel.activeModes.isEmpty()) {
+ val dnd = zenModeInteractor.dndMode.value
+ if (dnd == null) {
+ Log.wtf(TAG, "Triggered DND but it's null!?")
+ return
+ }
+ zenModeInteractor.activateMode(dnd)
+ } else {
+ zenModeInteractor.deactivateAllModes()
+ }
+ }
+
+ companion object {
+ const val TAG = "ModesTileUserActionInteractor"
+ }
}
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/ModesTileMapper.kt
index bac048f1786a..1507ef4b3b58 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/ModesTileMapper.kt
@@ -45,7 +45,11 @@ constructor(@ShadeDisplayAware private val resources: Resources, val theme: Reso
secondaryLabel = getModesStatus(data, resources)
contentDescription = "$label. $secondaryLabel"
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(
+ QSTileState.UserAction.CLICK,
+ QSTileState.UserAction.LONG_CLICK,
+ QSTileState.UserAction.TOGGLE_CLICK,
+ )
sideViewIcon = QSTileState.SideViewIcon.Chevron
expandedAccessibilityClass = Button::class
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
index 671943c5baff..d0f258052f04 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -20,18 +20,19 @@ import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.os.Bundle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.R
import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
class DataSaverDialogDelegate(
private val sysuiDialogFactory: SystemUIDialog.Factory,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val backgroundContext: CoroutineContext,
private val dataSaverController: DataSaverController,
private val sharedPreferences: SharedPreferences,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index b1b0001b6361..e8b9926e5cea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.viewmodel
import android.os.UserHandle
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import kotlinx.coroutines.flow.StateFlow
/**
@@ -37,6 +38,10 @@ interface QSTileViewModel {
/** Specifies whether this device currently supports this tile. */
val isAvailable: StateFlow<Boolean>
+ /** Specifies the [TileDetailsViewModel] for constructing the corresponding details view. */
+ val detailsViewModel: TileDetailsViewModel?
+ get() = null
+
/**
* Notifies about the user change. Implementations should avoid using 3rd party userId sources
* and use this value instead. This is to maintain consistent and concurrency-free behaviour
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index f9a1ad5d8424..632eeefcb462 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -27,6 +27,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
@@ -154,6 +155,10 @@ constructor(
qsTileViewModel.onUserChanged(UserHandle.of(currentUser))
}
+ override fun getDetailsViewModel(): TileDetailsViewModel? {
+ return qsTileViewModel.detailsViewModel
+ }
+
@Deprecated(
"Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
replaceWith = ReplaceWith("getMetricsSpec"),
@@ -207,8 +212,9 @@ constructor(
qsTileViewModel.destroy()
}
- override fun getState(): QSTile.State? =
+ override fun getState(): QSTile.State =
qsTileViewModel.currentState?.let { mapState(context, it, qsTileViewModel.config) }
+ ?: QSTile.State()
override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
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 62b120332289..91d907952bc6 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
@@ -22,6 +22,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -38,6 +39,7 @@ constructor(
val tileGridViewModel: TileGridViewModel,
val editModeViewModel: EditModeViewModel,
val detailsViewModel: DetailsViewModel,
+ val toolbarViewModelFactory: ToolbarViewModel.Factory,
) : ExclusiveActivatable() {
val brightnessSliderViewModel =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 95e7f56360c2..8c54ab40c680 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -25,7 +25,6 @@ import android.provider.Settings
import android.view.LayoutInflater
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -34,22 +33,23 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.user.ui.dialog.DialogShowerImpl
import javax.inject.Inject
import javax.inject.Provider
-/**
- * Controller for [UserDialog].
- */
+/** Controller for [UserDialog]. */
@SysUISingleton
-class UserSwitchDialogController @Inject constructor(
+class UserSwitchDialogController
+@Inject
+constructor(
private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager,
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val uiEventLogger: UiEventLogger,
- private val dialogFactory: SystemUIDialog.Factory
+ private val dialogFactory: SystemUIDialog.Factory,
) {
companion object {
@@ -64,7 +64,7 @@ class UserSwitchDialogController @Inject constructor(
* [userDetailViewAdapterProvider] and show it as launched from [expandable].
*/
fun showDialog(context: Context, expandable: Expandable) {
- with(dialogFactory.create()) {
+ with(dialogFactory.create(context)) {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
@@ -72,24 +72,31 @@ class UserSwitchDialogController @Inject constructor(
setPositiveButton(R.string.quick_settings_done) { _, _ ->
uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
}
- setNeutralButton(R.string.quick_settings_more_user_settings, { _, _ ->
- if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
- val controller = dialogTransitionAnimator.createActivityTransitionController(
- getButton(BUTTON_NEUTRAL)
- )
+ setNeutralButton(
+ R.string.quick_settings_more_user_settings,
+ { _, _ ->
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
+ val controller =
+ dialogTransitionAnimator.createActivityTransitionController(
+ getButton(BUTTON_NEUTRAL)
+ )
- if (controller == null) {
- dismiss()
- }
+ if (controller == null) {
+ dismiss()
+ }
- activityStarter.postStartActivityDismissingKeyguard(
- USER_SETTINGS_INTENT, 0, controller
- )
- }
- }, false /* dismissOnClick */)
- val gridFrame = LayoutInflater.from(this.context)
- .inflate(R.layout.qs_user_dialog_content, null)
+ activityStarter.postStartActivityDismissingKeyguard(
+ USER_SETTINGS_INTENT,
+ 0,
+ controller,
+ )
+ }
+ },
+ false, /* dismissOnClick */
+ )
+ val gridFrame =
+ LayoutInflater.from(this.context).inflate(R.layout.qs_user_dialog_content, null)
setView(gridFrame)
val adapter = userDetailViewAdapterProvider.get()
@@ -101,10 +108,7 @@ class UserSwitchDialogController @Inject constructor(
DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
)
if (controller != null) {
- dialogTransitionAnimator.show(
- this,
- controller,
- )
+ dialogTransitionAnimator.show(this, controller)
} else {
show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
index 2d6181aa04af..1355ba8bdfd4 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
@@ -47,7 +47,11 @@ internal constructor(
}
override fun createDialog(): SystemUIDialog {
- return systemUIDialogFactory.create(this, rearDisplayContext)
+ return systemUIDialogFactory.create(
+ this,
+ rearDisplayContext,
+ false, /* shouldAcsdDismissDialog */
+ )
}
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index d83d74e4e538..0e6fc36fb96a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -230,13 +230,7 @@ constructor(
) {
val currentSceneKey = currentScene.value
val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene
- if (
- !validateSceneChange(
- from = currentSceneKey,
- to = resolvedScene,
- loggingReason = loggingReason,
- )
- ) {
+ if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) {
return
}
@@ -268,13 +262,7 @@ constructor(
familyResolver.resolvedScene.value
}
} ?: toScene
- if (
- !validateSceneChange(
- from = currentSceneKey,
- to = resolvedScene,
- loggingReason = loggingReason,
- )
- ) {
+ if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) {
return
}
@@ -458,12 +446,11 @@ constructor(
* Will throw a runtime exception for illegal states (for example, attempting to change to a
* scene that's not part of the current scene framework configuration).
*
- * @param from The current scene being transitioned away from
* @param to The desired destination scene to transition to
* @param loggingReason The reason why the transition is requested, for logging purposes
* @return `true` if the scene change is valid; `false` if it shouldn't happen
*/
- private fun validateSceneChange(from: SceneKey, to: SceneKey, loggingReason: String): Boolean {
+ private fun validateSceneChange(to: SceneKey, loggingReason: String): Boolean {
if (to !in repository.allContentKeys) {
return false
}
@@ -486,7 +473,7 @@ constructor(
" Logging reason for scene change was: $loggingReason"
}
- return from != to
+ return true
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index b89eb5c762e0..2a0a22f32601 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -26,6 +26,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.combine
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@@ -34,7 +35,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
/**
@@ -59,6 +59,7 @@ constructor(
deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
keyguardInteractor.isDreamingWithOverlay,
+ keyguardInteractor.isAbleToDream,
transform = ::homeScene,
)
.stateIn(
@@ -71,6 +72,7 @@ constructor(
isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
isUnlocked = deviceEntryInteractor.isUnlocked.value,
isDreamingWithOverlay = false,
+ isAbleToDream = false,
),
)
@@ -82,10 +84,11 @@ constructor(
isDeviceEntered: Boolean,
isUnlocked: Boolean,
isDreamingWithOverlay: Boolean,
+ isAbleToDream: Boolean,
): SceneKey =
when {
// Dream can run even if Keyguard is disabled, thus it has the highest priority here.
- isDreamingWithOverlay -> Scenes.Dream
+ isDreamingWithOverlay && isAbleToDream -> Scenes.Dream
!isKeyguardEnabled -> Scenes.Gone
canSwipeToEnter == true -> Scenes.Lockscreen
!isDeviceEntered -> Scenes.Lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index 7d7cab41cf96..c45906840385 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -5,7 +5,6 @@ import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -35,7 +34,6 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
layoutInsetController: LayoutInsetsController,
sceneDataSourceDelegator: SceneDataSourceDelegator,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: AlternateBouncerDependencies,
) {
setLayoutInsetsController(layoutInsetController)
SceneWindowRootViewBinder.bind(
@@ -54,7 +52,6 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
},
dataSourceDelegator = sceneDataSourceDelegator,
qsSceneAdapter = qsSceneAdapter,
- alternateBouncerDependencies = alternateBouncerDependencies,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 1c15c74d5631..f7061d9af961 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -36,15 +36,12 @@ import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
-import com.android.systemui.keyguard.ui.composable.AlternateBouncer
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
@@ -77,7 +74,6 @@ object SceneWindowRootViewBinder {
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
dataSourceDelegator: SceneDataSourceDelegator,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: AlternateBouncerDependencies,
) {
val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
val sortedSceneByKey: Map<SceneKey, Scene> =
@@ -148,20 +144,10 @@ object SceneWindowRootViewBinder {
// the SceneContainerView. This SharedNotificationContainer should contain NSSL
// due to the NotificationStackScrollLayoutSection (legacy) or
// NotificationSection (scene container) moving it there.
- if (SceneContainerFlag.isEnabled) {
- (sharedNotificationContainer.parent as? ViewGroup)?.removeView(
- sharedNotificationContainer
- )
- view.addView(sharedNotificationContainer)
-
- // TODO(b/358354906): use an overlay for the alternate bouncer
- view.addView(
- createAlternateBouncerView(
- context = view.context,
- alternateBouncerDependencies = alternateBouncerDependencies,
- )
- )
- }
+ (sharedNotificationContainer.parent as? ViewGroup)?.removeView(
+ sharedNotificationContainer
+ )
+ view.addView(sharedNotificationContainer)
view.setSnapshotBinding { onVisibilityChangedInternal(viewModel.isVisible) }
awaitCancellation()
@@ -206,17 +192,6 @@ object SceneWindowRootViewBinder {
}
}
- private fun createAlternateBouncerView(
- context: Context,
- alternateBouncerDependencies: AlternateBouncerDependencies,
- ): ComposeView {
- return ComposeView(context).apply {
- setContent {
- AlternateBouncer(alternateBouncerDependencies = alternateBouncerDependencies)
- }
- }
- }
-
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 7b01c36489fb..a365b7c5e9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -37,7 +37,7 @@ import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -55,7 +55,7 @@ constructor(
private val activityManagerWrapper: ActivityManagerWrapper,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
- private val systemUiProxy: SystemUiProxy,
+ private val screenshotProxy: ScreenshotProxy,
private val displayTracker: DisplayTracker,
) {
/**
@@ -89,7 +89,7 @@ constructor(
CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
)
}
- systemUiProxy.dismissKeyguard()
+ screenshotProxy.dismissKeyguard()
var transitionOptions: ActivityOptions? = null
if (transitionCoordinator?.decor?.isAttachedToWindow == true) {
transitionCoordinator.startExit()
@@ -127,7 +127,7 @@ constructor(
private suspend fun launchCrossProfileIntent(
user: UserHandle,
intent: Intent,
- bundle: Bundle?
+ bundle: Bundle?,
) {
val connector = getCrossProfileConnector(user)
val completion = CompletableDeferred<Unit>()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 90695fa00ceb..253e3a613083 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -38,7 +38,7 @@ import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
import com.android.systemui.screenshot.message.MessageModule;
import com.android.systemui.screenshot.policy.ScreenshotPolicyModule;
-import com.android.systemui.screenshot.proxy.SystemUiProxyModule;
+import com.android.systemui.screenshot.proxy.ScreenshotProxyModule;
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel;
import dagger.Binds;
@@ -50,7 +50,7 @@ import dagger.multibindings.IntoMap;
/**
* Defines injectable resources for Screenshots
*/
-@Module(includes = {ScreenshotPolicyModule.class, SystemUiProxyModule.class, MessageModule.class})
+@Module(includes = {ScreenshotPolicyModule.class, ScreenshotProxyModule.class, MessageModule.class})
public abstract class ScreenshotModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
index e9599dcb026d..ec34808459c8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
@@ -22,21 +22,21 @@ import android.app.IActivityTaskManager
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenshot.data.model.DisplayContentModel
import com.android.systemui.screenshot.data.model.SystemUiState
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
/**
* Implements DisplayTaskRepository using [IActivityTaskManager], along with [ProfileTypeRepository]
- * and [SystemUiProxy].
+ * and [ScreenshotProxy].
*/
@SuppressLint("MissingPermission")
class DisplayContentRepositoryImpl
@Inject
constructor(
private val atmService: IActivityTaskManager,
- private val systemUiProxy: SystemUiProxy,
+ private val screenshotProxy: ScreenshotProxy,
@Background private val background: CoroutineDispatcher,
) : DisplayContentRepository {
@@ -53,8 +53,8 @@ constructor(
): DisplayContentModel {
return DisplayContentModel(
displayId,
- SystemUiState(systemUiProxy.isNotificationShadeExpanded()),
- rootTasks
+ SystemUiState(screenshotProxy.isNotificationShadeExpanded()),
+ rootTasks,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl
index e15030f78234..fb97fa71480b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.proxy;
interface IOnDoneCallback {
void onDone(boolean success);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl
index d2e3fbd65762..7b2f7e699521 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.proxy;
-import com.android.systemui.screenshot.IOnDoneCallback;
+import com.android.systemui.screenshot.proxy.IOnDoneCallback;
/** Interface implemented by ScreenshotProxyService */
interface IScreenshotProxy {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt
index e3eb3c4de80a..b44168f70ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt
@@ -24,7 +24,7 @@ package com.android.systemui.screenshot.proxy
*
* TODO: Rename and relocate 'ScreenshotProxyService' to this package and remove duplicate clients.
*/
-interface SystemUiProxy {
+interface ScreenshotProxy {
/** Indicate if the notification shade is "open"... (not in the fully collapsed position) */
suspend fun isNotificationShadeExpanded(): Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
index dcf58bde5f70..1158e2eb0ae5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
@@ -22,9 +22,6 @@ import android.content.Intent
import android.util.Log
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.screenshot.IOnDoneCallback
-import com.android.systemui.screenshot.IScreenshotProxy
-import com.android.systemui.screenshot.ScreenshotProxyService
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -32,8 +29,8 @@ import kotlinx.coroutines.CompletableDeferred
private const val TAG = "SystemUiProxy"
-/** An implementation of [SystemUiProxy] using [ScreenshotProxyService]. */
-class SystemUiProxyClient @Inject constructor(@Application context: Context) : SystemUiProxy {
+/** An implementation of [ScreenshotProxy] using [ScreenshotProxyService]. */
+class ScreenshotProxyClient @Inject constructor(@Application context: Context) : ScreenshotProxy {
@SuppressLint("ImplicitSamInstance")
private val proxyConnector: ServiceConnector<IScreenshotProxy> =
ServiceConnector.Impl(
@@ -41,7 +38,7 @@ class SystemUiProxyClient @Inject constructor(@Application context: Context) : S
Intent(context, ScreenshotProxyService::class.java),
Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
context.userId,
- IScreenshotProxy.Stub::asInterface
+ IScreenshotProxy.Stub::asInterface,
)
override suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k ->
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt
index 4dd5cc4b55b4..797be9121ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt
@@ -18,14 +18,13 @@ package com.android.systemui.screenshot.proxy
import android.app.Service
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.screenshot.ScreenshotProxyService
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
-interface SystemUiProxyModule {
+interface ScreenshotProxyModule {
@Binds
@IntoMap
@@ -34,5 +33,5 @@ interface SystemUiProxyModule {
@Binds
@SysUISingleton
- fun bindSystemUiProxy(systemUiProxyClient: SystemUiProxyClient): SystemUiProxy
+ fun bindSystemUiProxy(screenshotProxyClient: ScreenshotProxyClient): ScreenshotProxy
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
index 6df22f036273..a84f55268678 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot
+
+package com.android.systemui.screenshot.proxy
import android.content.Intent
import android.os.IBinder
@@ -67,7 +68,7 @@ constructor(
null,
true /* dismissShade */,
true /* afterKeyguardGone */,
- true /* deferred */
+ true, /* deferred */
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index c5c705c97b8d..4ad222d13840 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -162,7 +162,12 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
)
if (cutout == null) {
- screenshotStatic.setPadding(0, 0, 0, navBarInsets.bottom)
+ screenshotStatic.setPadding(
+ navBarInsets.left,
+ navBarInsets.top,
+ navBarInsets.right,
+ navBarInsets.bottom,
+ )
} else {
val waterfall = cutout.waterfallInsets
if (inPortrait) {
@@ -179,9 +184,9 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
)
} else {
screenshotStatic.setPadding(
- max(cutout.safeInsetLeft, waterfall.left),
+ max(cutout.safeInsetLeft, waterfall.left, navBarInsets.left),
waterfall.top,
- max(cutout.safeInsetRight, waterfall.right),
+ max(cutout.safeInsetRight, waterfall.right, navBarInsets.right),
max(
navBarInsets.bottom + verticalPadding,
waterfall.bottom + verticalPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 9a1ffcbab8d1..a7b51faaed57 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -35,6 +35,7 @@ import android.view.animation.DecelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.window.flag.WindowBlurFlag;
/**
* Drawable used on SysUI scrims.
@@ -213,6 +214,11 @@ public class ScrimDrawable extends Drawable {
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
mPaint.setAlpha(mAlpha);
+ if (WindowBlurFlag.isEnabled()) {
+ // TODO(b/370555223): Match the alpha to the visual spec when it is finalized.
+ // TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
+ mPaint.setAlpha((int) (0.5f * mAlpha));
+ }
if (mConcaveInfo != null) {
drawConcave(canvas);
} else if (mCornerRadiusEnabled && mCornerRadius > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 49f3cfc4ceaf..4bfa61e9dcd4 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -44,6 +44,8 @@ import com.android.systemui.util.LargeScreenUtils;
import java.util.concurrent.Executor;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
/**
* A view which can draw a scrim. This view maybe be used in multiple windows running on different
* threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
@@ -250,6 +252,10 @@ public class ScrimView extends View {
if (mBlendWithMainColor) {
mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
}
+ if (notificationShadeBlur()) {
+ // TODO(b/370555223): Fix color and transparency to match visual spec exactly
+ mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), Color.GRAY, 0.5f);
+ }
drawable.setColor(mainTinted, animated);
} else {
boolean hasAlpha = Color.alpha(mTintColor) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index fc4db0877dbe..b7a3aedc565e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -31,6 +31,8 @@ import android.os.UserManager
import android.util.Log
import androidx.annotation.GuardedBy
import androidx.annotation.WorkerThread
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.app.tracing.traceSection
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
@@ -49,7 +51,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.sync.Mutex
/**
@@ -314,7 +315,9 @@ internal constructor(
list.forEach {
val callback = it.callback.get()
if (callback != null) {
- it.executor.execute { action(callback) { latch.countDown() } }
+ it.executor.execute {
+ traceSection({ "$callback" }) { action(callback) { latch.countDown() } }
+ }
} else {
latch.countDown()
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 90d27f4b33e9..c65c3b854b82 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -21,7 +21,6 @@ import static com.android.settingslib.display.BrightnessUtils.convertGammaToLine
import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat;
import android.animation.ValueAnimator;
-import android.annotation.NonNull;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.BrightnessInfo;
@@ -42,6 +41,7 @@ import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.MathUtils;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -72,19 +72,19 @@ import java.util.concurrent.Executor;
public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
private static final String TAG = "CentralSurfaces.BrightnessController";
- private static final int SLIDER_ANIMATION_DURATION = 3000;
+ protected static final int SLIDER_ANIMATION_DURATION = 3000;
- private static final int MSG_UPDATE_SLIDER = 1;
- private static final int MSG_ATTACH_LISTENER = 2;
- private static final int MSG_DETACH_LISTENER = 3;
- private static final int MSG_VR_MODE_CHANGED = 4;
+ protected static final int MSG_UPDATE_SLIDER = 1;
+ protected static final int MSG_ATTACH_LISTENER = 2;
+ protected static final int MSG_DETACH_LISTENER = 3;
+ protected static final int MSG_VR_MODE_CHANGED = 4;
- private static final Uri BRIGHTNESS_MODE_URI =
+ protected static final Uri BRIGHTNESS_MODE_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
private final int mDisplayId;
private final Context mContext;
- private final ToggleSlider mControl;
+ protected final ToggleSlider mControl;
private final DisplayManager mDisplayManager;
private final UserTracker mUserTracker;
private final DisplayTracker mDisplayTracker;
@@ -109,10 +109,10 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
private boolean mTrackingTouch = false; // Brightness adjusted via touch events.
private volatile boolean mIsVrModeEnabled;
private boolean mListening;
- private boolean mExternalChange;
+ protected boolean mExternalChange;
private boolean mControlValueInitialized;
- private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
- private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
+ protected float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
+ protected float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
private boolean mIsBrightnessOverriddenByWindow = false;
private ValueAnimator mSliderAnimator;
@@ -253,10 +253,8 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
if (info == null) {
return;
}
- mBrightnessMax = info.brightnessMaximum;
- mBrightnessMin = info.brightnessMinimum;
- mIsBrightnessOverriddenByWindow = info.isBrightnessOverrideByWindow;
+ updateBrightnessInfo(info);
// Value is passed as intbits, since this is what the message takes.
final int valueAsIntBits = Float.floatToIntBits(info.brightness);
mMainHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
@@ -264,6 +262,12 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
}
};
+ protected void updateBrightnessInfo(BrightnessInfo info) {
+ mBrightnessMax = info.brightnessMaximum;
+ mBrightnessMin = info.brightnessMinimum;
+ mIsBrightnessOverriddenByWindow = info.isBrightnessOverrideByWindow;
+ }
+
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
@@ -301,7 +305,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
}
};
- private final Handler mMainHandler;
+ protected final Handler mMainHandler;
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@@ -459,7 +463,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
return !mAutomatic && !mTrackingTouch;
}
- private void updateSlider(float brightnessValue, boolean inVrMode) {
+ protected void updateSlider(float brightnessValue, boolean inVrMode) {
final float min = mBrightnessMin;
final float max = mBrightnessMax;
@@ -502,12 +506,17 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
mSliderAnimator.start();
}
-
+ /** Factory interface for creating a {@link BrightnessController}. */
+ public interface Factory {
+ @NonNull
+ BrightnessController create(ToggleSlider toggleSlider);
+ }
/** Factory for creating a {@link BrightnessController}. */
@AssistedFactory
- public interface Factory {
+ public interface BrightnessControllerFactory extends Factory {
/** Create a {@link BrightnessController} */
+ @NonNull
BrightnessController create(ToggleSlider toggleSlider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 503d0bfbc301..02eca74fd751 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -25,6 +25,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
@@ -42,6 +43,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.time.SystemClock;
+
import com.google.android.msdl.domain.MSDLPlayer;
import javax.inject.Inject;
@@ -89,7 +91,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
}
};
- BrightnessSliderController(
+ protected BrightnessSliderController(
BrightnessSliderView brightnessSliderView,
FalsingManager falsingManager,
UiEventLogger uiEventLogger,
@@ -247,16 +249,20 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
return mView.isVisibleToUser();
}
+ protected void handleSliderProgressChange(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mListener != null) {
+ mListener.onChanged(mTracking, progress, false);
+ if (fromUser) {
+ mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
+ }
+ }
+ }
+
private final SeekBar.OnSeekBarChangeListener mSeekListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (mListener != null) {
- mListener.onChanged(mTracking, progress, false);
- if (fromUser) {
- mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
- }
- }
+ handleSliderProgressChange(seekBar, progress, fromUser);
}
@Override
@@ -289,11 +295,21 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
}
};
+ /** Factory interface for creating a {@link BrightnessSliderController}. */
+ public interface Factory {
+ @NonNull
+ BrightnessSliderController create(
+ Context context,
+ @Nullable ViewGroup viewRoot);
+
+ int getLayout();
+ }
+
/**
* Creates a {@link BrightnessSliderController} with its associated view.
*/
- public static class Factory {
+ public static class BrightnessSliderControllerFactory implements Factory {
private final FalsingManager mFalsingManager;
private final UiEventLogger mUiEventLogger;
private final VibratorHelper mVibratorHelper;
@@ -303,7 +319,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
private final BrightnessWarningToast mBrightnessWarningToast;
@Inject
- public Factory(
+ public BrightnessSliderControllerFactory(
FalsingManager falsingManager,
UiEventLogger uiEventLogger,
VibratorHelper vibratorHelper,
@@ -328,6 +344,8 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
* @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
* hierarchy will not be attached
*/
+ @Override
+ @NonNull
public BrightnessSliderController create(
Context context,
@Nullable ViewGroup viewRoot) {
@@ -345,7 +363,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
}
/** Get the layout to inflate based on what slider to use */
- private int getLayout() {
+ public int getLayout() {
return R.layout.quick_settings_brightness_dialog;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index a39d25a5ef30..550ac62c0bde 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -43,12 +43,12 @@ import java.util.Collections;
public class BrightnessSliderView extends FrameLayout {
@NonNull
- private ToggleSeekBar mSlider;
+ protected ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
private Gefingerpoken mOnInterceptListener;
@Nullable
- private Drawable mProgressDrawable;
- private float mScale = 1f;
+ protected Drawable mProgressDrawable;
+ protected float mScale = 1f;
private final Rect mSystemGestureExclusionRect = new Rect();
public BrightnessSliderView(Context context) {
@@ -65,6 +65,10 @@ public class BrightnessSliderView extends FrameLayout {
super.onFinishInflate();
setLayerType(LAYER_TYPE_HARDWARE, null);
+ initBrightnessViewComponents();
+ }
+
+ protected void initBrightnessViewComponents() {
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
setBoundaryOffset();
@@ -81,7 +85,7 @@ public class BrightnessSliderView extends FrameLayout {
}
}
- private void setBoundaryOffset() {
+ protected void setBoundaryOffset() {
// BrightnessSliderView uses hardware layer; if the background of its children exceed its
// boundary, it'll be cropped. We need to expand its boundary so that the background of
// ToggleSeekBar (i.e. the focus state) can be correctly rendered.
@@ -131,7 +135,7 @@ public class BrightnessSliderView extends FrameLayout {
* @param admin
* @see ToggleSeekBar#setEnforcedAdmin
*/
- void setAdminBlocker(ToggleSeekBar.AdminBlocker blocker) {
+ protected void setAdminBlocker(ToggleSeekBar.AdminBlocker blocker) {
mSlider.setAdminBlocker(blocker);
}
@@ -211,7 +215,7 @@ public class BrightnessSliderView extends FrameLayout {
}
}
- private void applySliderScale() {
+ protected void applySliderScale() {
if (mProgressDrawable != null) {
final Rect r = mProgressDrawable.getBounds();
int height = (int) (mProgressDrawable.getIntrinsicHeight() * mScale);
@@ -229,7 +233,7 @@ public class BrightnessSliderView extends FrameLayout {
* Interface to attach a listener for {@link View#dispatchTouchEvent}.
*/
@FunctionalInterface
- interface DispatchTouchEventListener {
+ public interface DispatchTouchEventListener {
boolean onDispatchTouchEvent(MotionEvent ev);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 30b6892731f1..a0985fc61b07 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -62,7 +62,7 @@ public class ToggleSeekBar extends SeekBar {
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
setHovered(false);
}
- return true;
+ return super.onHoverEvent(event);
}
@Override
@@ -85,12 +85,12 @@ public class ToggleSeekBar extends SeekBar {
}
}
- void setAdminBlocker(AdminBlocker blocker) {
+ public void setAdminBlocker(AdminBlocker blocker) {
mAdminBlocker = blocker;
setEnabled(blocker == null);
}
- interface AdminBlocker {
+ public interface AdminBlocker {
boolean block();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt
new file mode 100644
index 000000000000..fbe442eb4b5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.settings.brightness.dagger
+
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.settings.brightness.BrightnessSliderController
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class BrightnessSliderModule {
+
+ @Binds
+ abstract fun bindBrightnessSliderControllerFactory(
+ factory: BrightnessSliderController.BrightnessSliderControllerFactory
+ ): BrightnessSliderController.Factory
+
+ @Binds
+ abstract fun bindBrightnessControllerFactory(
+ factory: BrightnessController.BrightnessControllerFactory
+ ): BrightnessController.Factory
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 31780a56f7f0..61ac1a029f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -41,11 +41,11 @@ import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Flags
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -83,6 +83,7 @@ class GlanceableHubContainerController
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val communalViewModel: CommunalViewModel,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -514,7 +515,7 @@ constructor(
val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt())
val touchOnSmartspace =
lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt())
- val glanceableHubV2 = communalHubOnMobile()
+ val glanceableHubV2 = communalSettingsInteractor.isV2FlagEnabled()
if (
!hubShowing &&
(touchOnNotifications || touchOnUmo || touchOnSmartspace || glanceableHubV2)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3a6c250e55f1..d347084f435d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3185,7 +3185,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return false;
}
if (mHeadsUpAppearanceController != null
- && mHeadsUpAppearanceController.shouldBeVisible()) {
+ && mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()) {
return false;
}
return !mShowIconsWhenExpanded;
@@ -4634,7 +4634,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public boolean shouldHeadsUpBeVisible() {
return mHeadsUpAppearanceController != null &&
- mHeadsUpAppearanceController.shouldBeVisible();
+ mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index f5fc1f414f82..0a9aa9b49e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -166,8 +166,22 @@ public class NotificationShadeWindowView extends WindowRootView {
}
@Override
+ public void onMovedToDisplay(int displayId, Configuration config) {
+ super.onMovedToDisplay(displayId, config);
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
+ ShadeTraceLogger.INSTANCE.logOnMovedToDisplay(displayId, config);
+ if (mConfigurationForwarder != null) {
+ mConfigurationForwarder.dispatchOnMovedToDisplay(displayId, config);
+ }
+ // When the window is moved we're only receiving a call to this method instead of the
+ // onConfigurationChange itself. Let's just trigegr a normal config change.
+ onConfigurationChanged(config);
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ ShadeTraceLogger.INSTANCE.logOnConfigChanged(newConfig);
if (mConfigurationForwarder != null) {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
mConfigurationForwarder.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index ff39a3ddc17c..d31868ca0217 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -22,15 +22,16 @@ import android.view.LayoutInflater
import android.view.WindowManager
import android.view.WindowManager.LayoutParams
import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
+import android.window.WindowContext
import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.ConfigurationStateImpl
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
@@ -48,6 +49,7 @@ import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Provider
+import javax.inject.Qualifier
/**
* Module responsible for managing display-specific components and resources for the notification
@@ -81,6 +83,19 @@ object ShadeDisplayAwareModule {
@Provides
@ShadeDisplayAware
@SysUISingleton
+ fun provideShadeDisplayAwareWindowContext(@ShadeDisplayAware context: Context): WindowContext {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ // We rely on the fact context is a WindowContext as the API to reparent windows is only
+ // available there.
+ return (context as? WindowContext)
+ ?: error(
+ "ShadeDisplayAware context must be a window context to allow window reparenting."
+ )
+ }
+
+ @Provides
+ @ShadeDisplayAware
+ @SysUISingleton
fun provideShadeWindowLayoutParams(@ShadeDisplayAware context: Context): LayoutParams {
return ShadeWindowLayoutParams.create(context)
}
@@ -119,7 +134,7 @@ object ShadeDisplayAwareModule {
fun provideShadeWindowConfigurationController(
@ShadeDisplayAware shadeContext: Context,
factory: ConfigurationControllerImpl.Factory,
- @GlobalConfig globalConfigController: ConfigurationController,
+ @Main globalConfigController: ConfigurationController,
): ConfigurationController {
return if (ShadeWindowGoesAround.isEnabled) {
factory.create(shadeContext)
@@ -145,7 +160,7 @@ object ShadeDisplayAwareModule {
factory: ConfigurationStateImpl.Factory,
@ShadeDisplayAware configurationController: ConfigurationController,
@ShadeDisplayAware context: Context,
- @GlobalConfig configurationState: ConfigurationState,
+ @Main configurationState: ConfigurationState,
): ConfigurationState {
return if (ShadeWindowGoesAround.isEnabled) {
factory.create(context, configurationController)
@@ -161,7 +176,7 @@ object ShadeDisplayAwareModule {
factory: ConfigurationRepositoryImpl.Factory,
@ShadeDisplayAware configurationController: ConfigurationController,
@ShadeDisplayAware context: Context,
- @GlobalConfig globalConfigurationRepository: ConfigurationRepository,
+ @Main globalConfigurationRepository: ConfigurationRepository,
): ConfigurationRepository {
return if (ShadeWindowGoesAround.isEnabled) {
factory.create(context, configurationController)
@@ -175,7 +190,7 @@ object ShadeDisplayAwareModule {
@ShadeDisplayAware
fun provideShadeAwareConfigurationInteractor(
@ShadeDisplayAware configurationRepository: ConfigurationRepository,
- @GlobalConfig configurationInteractor: ConfigurationInteractor,
+ @Main configurationInteractor: ConfigurationInteractor,
): ConfigurationInteractor {
return if (ShadeWindowGoesAround.isEnabled) {
ConfigurationInteractorImpl(configurationRepository)
@@ -203,7 +218,9 @@ object ShadeDisplayAwareModule {
@Provides
@IntoMap
@ClassKey(ShadePrimaryDisplayCommand::class)
- fun provideShadePrimaryDisplayCommand(impl: Provider<ShadePrimaryDisplayCommand>): CoreStartable {
+ fun provideShadePrimaryDisplayCommand(
+ impl: Provider<ShadePrimaryDisplayCommand>
+ ): CoreStartable {
return if (ShadeWindowGoesAround.isEnabled) {
impl.get()
} else {
@@ -221,9 +238,23 @@ object ShadeDisplayAwareModule {
CoreStartable.NOP
}
}
+
+ @Provides
+ @ShadeOnDefaultDisplayWhenLocked
+ fun provideShadeOnDefaultDisplayWhenLocked(): Boolean = true
}
@Module
internal interface OptionalShadeDisplayAwareBindings {
@BindsOptionalOf fun bindOptionalOfWindowRootView(): WindowRootView
}
+
+/**
+ * Annotates the boolean value that defines whether the shade window should go back to the default
+ * display when the keyguard is visible.
+ *
+ * As of today (Dec 2024), This is a configuration parameter provided in the dagger graph as the
+ * final policy around keyguard display is still under discussion, and will be evaluated based on
+ * how well this solution behaves from the performance point of view.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeOnDefaultDisplayWhenLocked
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
index a54f6b9c6743..7bfe40c3d811 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -16,23 +16,23 @@
package com.android.systemui.shade
-import android.view.Display
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.settings.GlobalSettings
import java.io.PrintWriter
import javax.inject.Inject
-import kotlin.text.toIntOrNull
@SysUISingleton
class ShadePrimaryDisplayCommand
@Inject
constructor(
+ private val globalSettings: GlobalSettings,
private val commandRegistry: CommandRegistry,
private val displaysRepository: DisplayRepository,
private val positionRepository: MutableShadeDisplaysRepository,
@@ -45,7 +45,7 @@ constructor(
}
override fun help(pw: PrintWriter) {
- pw.println("shade_display_override (<displayId>|<policyName>) ")
+ pw.println("shade_display_override <policyName> ")
pw.println("Set the display which is holding the shade, or the policy that defines it.")
pw.println()
pw.println("shade_display_override policies")
@@ -56,9 +56,6 @@ constructor(
pw.println()
pw.println("shade_display_override (list|status) ")
pw.println("Lists available displays and which has the shade")
- pw.println()
- pw.println("shade_display_override any_external")
- pw.println("Moves the shade to the first not-default display available")
}
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -74,28 +71,24 @@ constructor(
fun execute() {
when (val command = args.getOrNull(0)?.lowercase()) {
"reset" -> reset()
+ "policies" -> printPolicies()
"list",
"status" -> printStatus()
- "policies" -> printPolicies()
- "any_external" -> anyExternal()
null -> help(pw)
else -> parsePolicy(command)
}
}
private fun parsePolicy(policyIdentifier: String) {
- val displayId = policyIdentifier.toIntOrNull()
- when {
- displayId != null -> changeDisplay(displayId = displayId)
- policies.any { it.name == policyIdentifier } -> {
- positionRepository.policy.value = policies.first { it.name == policyIdentifier }
- }
- else -> help(pw)
+ if (policies.any { it.name == policyIdentifier }) {
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, policyIdentifier)
+ } else {
+ help(pw)
}
}
private fun reset() {
- positionRepository.policy.value = defaultPolicy
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}")
}
@@ -117,30 +110,5 @@ constructor(
pw.println(if (currentPolicyName == it.name) " (Current policy)" else "")
}
}
-
- private fun anyExternal() {
- val anyExternalDisplay =
- displaysRepository.displays.value.firstOrNull {
- it.displayId != Display.DEFAULT_DISPLAY
- }
- if (anyExternalDisplay == null) {
- pw.println("No external displays available.")
- return
- }
- setDisplay(anyExternalDisplay.displayId)
- }
-
- private fun changeDisplay(displayId: Int) {
- if (displayId < 0) {
- pw.println("Error: display id should be positive integer")
- }
-
- setDisplay(displayId)
- }
-
- private fun setDisplay(id: Int) {
- positionRepository.policy.value = SpecificDisplayIdPolicy(id)
- pw.println("New shade primary display id is $id")
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
new file mode 100644
index 000000000000..a596d4f64638
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.shade
+
+import android.content.res.Configuration
+import android.os.Trace
+import com.android.app.tracing.TraceUtils.traceAsync
+
+/**
+ * Centralized logging for shade-related events to a dedicated Perfetto track.
+ *
+ * Used by shade components to log events to a track named [TAG]. This consolidates shade-specific
+ * events into a single track for easier analysis in Perfetto, rather than scattering them across
+ * various threads' logs.
+ */
+object ShadeTraceLogger {
+ private const val TAG = "ShadeTraceLogger"
+
+ fun logOnMovedToDisplay(displayId: Int, config: Configuration) {
+ if (!Trace.isEnabled()) return
+ Trace.instantForTrack(
+ Trace.TRACE_TAG_APP,
+ TAG,
+ "onMovedToDisplay(displayId=$displayId, dpi=" + config.densityDpi + ")",
+ )
+ }
+
+ fun logOnConfigChanged(config: Configuration) {
+ if (!Trace.isEnabled()) return
+ Trace.instantForTrack(
+ Trace.TRACE_TAG_APP,
+ TAG,
+ "onConfigurationChanged(dpi=" + config.densityDpi + ")",
+ )
+ }
+
+ fun logMoveShadeWindowTo(displayId: Int) {
+ if (!Trace.isEnabled()) return
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "moveShadeWindowTo(displayId=$displayId)")
+ }
+
+ fun traceReparenting(r: () -> Unit) {
+ traceAsync(TAG, { "reparenting" }) { r() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 15b22700072f..8937ce33cd38 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -33,7 +33,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
@@ -91,7 +90,6 @@ abstract class ShadeViewProviderModule {
layoutInsetController: NotificationInsetsController,
sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
): WindowRootView {
return if (SceneContainerFlag.isEnabled) {
checkNoSceneDuplicates(scenesProvider.get())
@@ -107,7 +105,6 @@ abstract class ShadeViewProviderModule {
layoutInsetController = layoutInsetController,
sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
qsSceneAdapter = qsSceneAdapter,
- alternateBouncerDependencies = alternateBouncerDependencies.get(),
)
sceneWindowRootView
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt
index ae36e81c7b1f..29c7aa0680d3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt
@@ -21,13 +21,18 @@ import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
/** Accepts touch events, detects long press, and calls ShadeViewController#onStatusBarLongPress. */
@SysUISingleton
class StatusBarLongPressGestureDetector
@Inject
-constructor(context: Context, val shadeViewController: ShadeViewController) {
+constructor(
+ // TODO b/383125226 - Make this class per-display
+ @Main context: Context,
+ val shadeViewController: ShadeViewController,
+) {
val gestureDetector =
GestureDetector(
context,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index 756241e9b071..af48231e0a99 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -16,17 +16,22 @@
package com.android.systemui.shade.data.repository
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
/** Source of truth for the display currently holding the shade. */
@@ -38,7 +43,7 @@ interface ShadeDisplaysRepository {
/** Allows to change the policy that determines in which display the Shade window is visible. */
interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
/** Updates the policy to select where the shade is visible. */
- val policy: MutableStateFlow<ShadeDisplayPolicy>
+ val policy: StateFlow<ShadeDisplayPolicy>
}
/** Keeps the policy and propagates the display id for the shade from it. */
@@ -46,9 +51,27 @@ interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
@OptIn(ExperimentalCoroutinesApi::class)
class ShadeDisplaysRepositoryImpl
@Inject
-constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) :
- MutableShadeDisplaysRepository {
- override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy)
+constructor(
+ globalSettings: GlobalSettings,
+ defaultPolicy: ShadeDisplayPolicy,
+ @Background bgScope: CoroutineScope,
+ policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
+) : MutableShadeDisplaysRepository {
+
+ override val policy: StateFlow<ShadeDisplayPolicy> =
+ globalSettings
+ .observerFlow(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
+ .onStart { emit(Unit) }
+ .map {
+ val current = globalSettings.getString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
+ for (policy in policies) {
+ if (policy.name == current) return@map policy
+ }
+ globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
+ return@map defaultPolicy
+ }
+ .distinctUntilChanged()
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), defaultPolicy)
override val displayId: StateFlow<Int> =
policy
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt
index d43aad70368e..3819c6ffae08 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt
@@ -21,12 +21,9 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-/** Policy to specify a display id explicitly. */
-open class SpecificDisplayIdPolicy(id: Int) : ShadeDisplayPolicy {
- override val name: String = "display_${id}_policy"
+/** Policy to specify a default display explicitly. */
+class DefaultDisplayShadePolicy @Inject constructor() : ShadeDisplayPolicy {
+ override val name: String = "default_display"
- override val displayId: StateFlow<Int> = MutableStateFlow(id)
+ override val displayId: StateFlow<Int> = MutableStateFlow(Display.DEFAULT_DISPLAY)
}
-
-class DefaultDisplayShadePolicy @Inject constructor() :
- SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index bb96b0b3ce50..17b5e5b584b4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -23,6 +23,10 @@ import kotlinx.coroutines.flow.StateFlow
/** Describes the display the shade should be shown in. */
interface ShadeDisplayPolicy {
+ /**
+ * String used to identify each policy and used to set policy via adb command. This value must
+ * match a value defined in the SettingsLib shade_display_awareness_values string array.
+ */
val name: String
/** The display id the shade should be at, according to this policy. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
index 22e9487af84c..30b086f03d66 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -22,34 +22,55 @@ import com.android.app.tracing.coroutines.launchTraced
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.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* Moves the shade on the last display that received a status bar touch.
*
- * If the display is removed, falls back to the default one.
+ * If the display is removed, falls back to the default one. When [shadeOnDefaultDisplayWhenLocked]
+ * is true, the shade falls back to the default display when the keyguard is visible.
*/
@SysUISingleton
class StatusBarTouchShadeDisplayPolicy
@Inject
-constructor(displayRepository: DisplayRepository, @Background val backgroundScope: CoroutineScope) :
- ShadeDisplayPolicy {
- override val name: String
- get() = "status_bar_latest_touch"
+constructor(
+ displayRepository: DisplayRepository,
+ keyguardRepository: KeyguardRepository,
+ @Background val backgroundScope: CoroutineScope,
+ @ShadeOnDefaultDisplayWhenLocked val shadeOnDefaultDisplayWhenLocked: Boolean,
+) : ShadeDisplayPolicy {
+ override val name: String = "status_bar_latest_touch"
private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds
- override val displayId: StateFlow<Int>
- get() = currentDisplayId
+ override val displayId: StateFlow<Int> =
+ if (shadeOnDefaultDisplayWhenLocked) {
+ keyguardRepository.isKeyguardShowing
+ .combine(currentDisplayId) { isKeyguardShowing, currentDisplayId ->
+ if (isKeyguardShowing) {
+ Display.DEFAULT_DISPLAY
+ } else {
+ currentDisplayId
+ }
+ }
+ .stateIn(backgroundScope, SharingStarted.WhileSubscribed(), currentDisplayId.value)
+ } else {
+ currentDisplayId
+ }
private var removalListener: Job? = null
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index 34148671cf2a..8d536accaf76 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -16,10 +16,8 @@
package com.android.systemui.shade.domain.interactor
-import android.content.Context
import android.util.Log
-import android.view.WindowManager
-import android.view.WindowManager.LayoutParams
+import android.window.WindowContext
import androidx.annotation.UiThread
import com.android.app.tracing.coroutines.launchTraced
import com.android.app.tracing.traceSection
@@ -29,6 +27,8 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.ShadeTraceLogger.logMoveShadeWindowTo
+import com.android.systemui.shade.ShadeTraceLogger.traceReparenting
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.util.kotlin.getOrNull
@@ -45,9 +45,7 @@ class ShadeDisplaysInteractor
constructor(
optionalShadeRootView: Optional<WindowRootView>,
private val shadePositionRepository: ShadeDisplaysRepository,
- @ShadeDisplayAware private val shadeContext: Context,
- @ShadeDisplayAware private val shadeLayoutParams: LayoutParams,
- @ShadeDisplayAware private val wm: WindowManager,
+ @ShadeDisplayAware private val shadeContext: WindowContext,
@Background private val bgScope: CoroutineScope,
@Main private val mainThreadContext: CoroutineContext,
) : CoreStartable {
@@ -72,7 +70,12 @@ constructor(
/** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */
private suspend fun moveShadeWindowTo(destinationId: Int) {
Log.d(TAG, "Trying to move shade window to display with id $destinationId")
- val currentDisplay = shadeRootView.display
+ logMoveShadeWindowTo(destinationId)
+ // Why using the shade context here instead of the view's Display?
+ // The context's display is updated before the view one, so it is a better indicator of
+ // which display the shade is supposed to be at. The View display is updated after the first
+ // rendering with the new config.
+ val currentDisplay = shadeContext.display
if (currentDisplay == null) {
Log.w(TAG, "Current shade display is null")
return
@@ -83,7 +86,9 @@ constructor(
return
}
try {
- withContext(mainThreadContext) { moveShadeWindow(toId = destinationId) }
+ withContext(mainThreadContext) {
+ traceReparenting { reparentToDisplayId(id = destinationId) }
+ }
} catch (e: IllegalStateException) {
Log.e(
TAG,
@@ -94,25 +99,8 @@ constructor(
}
@UiThread
- private fun moveShadeWindow(toId: Int) {
- traceSection({ "moveShadeWindow to $toId" }) {
- removeShadeWindow()
- updateContextDisplay(toId)
- addShadeWindow()
- }
- }
-
- @UiThread
- private fun removeShadeWindow(): Unit =
- traceSection("removeShadeWindow") { wm.removeView(shadeRootView) }
-
- @UiThread
- private fun addShadeWindow(): Unit =
- traceSection("addShadeWindow") { wm.addView(shadeRootView, shadeLayoutParams) }
-
- @UiThread
- private fun updateContextDisplay(newDisplayId: Int) {
- traceSection("updateContextDisplay") { shadeContext.updateDisplay(newDisplayId) }
+ private fun reparentToDisplayId(id: Int) {
+ traceSection({ "reparentToDisplayId(id=$id)" }) { shadeContext.reparentToDisplay(id) }
}
private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 6bbd4a504471..d343ed5ab599 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -35,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import java.io.PrintWriter
import javax.inject.Inject
+import com.android.systemui.Flags.notificationShadeBlur
@SysUISingleton
open class BlurUtils @Inject constructor(
@@ -43,7 +44,14 @@ open class BlurUtils @Inject constructor(
dumpManager: DumpManager
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
- val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+ val maxBlurRadius =
+ if (notificationShadeBlur()) {
+ resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
+ } else {
+ resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+ }
+
+
private var lastAppliedBlur = 0
private var earlyWakeupEnabled = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index c997ac5ad9df..2544323d83d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -20,7 +20,6 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static com.android.systemui.Flags.fetchBookmarksXmlKeyboardShortcuts;
-import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -78,7 +77,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
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.statusbar.phone.CentralSurfaces;
@@ -428,9 +426,7 @@ public final class KeyboardShortcutListSearch {
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
} else {
mCurrentAppPackageName = result.get(0).getPackageName();
- if (validateKeyboardShortcutHelperIconUri()) {
- KeyboardShortcuts.sanitiseShortcuts(result);
- }
+ KeyboardShortcuts.sanitiseShortcuts(result);
mSpecificAppGroup.addAll(
reMapToKeyboardShortcutMultiMappingGroup(result));
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
@@ -446,9 +442,7 @@ public final class KeyboardShortcutListSearch {
// Add specific Ime shortcuts
if (result != null) {
if (!result.isEmpty()) {
- if (validateKeyboardShortcutHelperIconUri()) {
- KeyboardShortcuts.sanitiseShortcuts(result);
- }
+ KeyboardShortcuts.sanitiseShortcuts(result);
mInputGroup.addAll(
reMapToKeyboardShortcutMultiMappingGroup(result));
}
@@ -768,8 +762,6 @@ public final class KeyboardShortcutListSearch {
Intent.CATEGORY_APP_EMAIL,
Intent.CATEGORY_APP_CALENDAR,
Intent.CATEGORY_APP_MAPS,
- Intent.CATEGORY_APP_MUSIC,
- Intent.CATEGORY_APP_MESSAGING,
Intent.CATEGORY_APP_CALCULATOR,
};
String[] shortcutLabels = {
@@ -778,19 +770,15 @@ public final class KeyboardShortcutListSearch {
mContext.getString(R.string.keyboard_shortcut_group_applications_email),
mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
mContext.getString(R.string.keyboard_shortcut_group_applications_maps),
- mContext.getString(R.string.keyboard_shortcut_group_applications_music),
- mContext.getString(R.string.keyboard_shortcut_group_applications_sms),
mContext.getString(R.string.keyboard_shortcut_group_applications_calculator)
};
int[] keyCodes = {
KeyEvent.KEYCODE_B,
- KeyEvent.KEYCODE_C,
+ KeyEvent.KEYCODE_P,
KeyEvent.KEYCODE_E,
- KeyEvent.KEYCODE_K,
+ KeyEvent.KEYCODE_C,
KeyEvent.KEYCODE_M,
- KeyEvent.KEYCODE_P,
- KeyEvent.KEYCODE_S,
KeyEvent.KEYCODE_U,
};
@@ -1422,13 +1410,11 @@ public final class KeyboardShortcutListSearch {
}
private int getColorOfTextColorOnAccent() {
- return Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.materialColorOnPrimary);
+ return mContext.getColor(com.android.internal.R.color.materialColorOnPrimary);
}
private int getColorOfTextColorSecondary() {
- return Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.materialColorOnSurface);
+ return mContext.getColor(com.android.internal.R.color.materialColorOnSurface);
}
// Create the new data structure for handling the N-to-1 key mapping and other complex case.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 766c391b14d8..2ed168aa82e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -21,7 +21,6 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static com.android.systemui.Flags.fetchBookmarksXmlKeyboardShortcuts;
-import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -412,10 +411,7 @@ public final class KeyboardShortcuts {
mReceivedAppShortcutGroups =
result == null ? Collections.emptyList() : result;
- if (validateKeyboardShortcutHelperIconUri()) {
- sanitiseShortcuts(mReceivedAppShortcutGroups);
- }
-
+ sanitiseShortcuts(mReceivedAppShortcutGroups);
maybeMergeAndShowKeyboardShortcuts();
}
@@ -423,10 +419,7 @@ public final class KeyboardShortcuts {
mReceivedImeShortcutGroups =
result == null ? Collections.emptyList() : result;
- if (validateKeyboardShortcutHelperIconUri()) {
- sanitiseShortcuts(mReceivedImeShortcutGroups);
- }
-
+ sanitiseShortcuts(mReceivedImeShortcutGroups);
maybeMergeAndShowKeyboardShortcuts();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 71efbab0f738..3180a06ae787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -14,19 +14,49 @@
package com.android.systemui.statusbar;
+import android.annotation.IntDef;
import android.content.pm.UserInfo;
import android.util.SparseArray;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public interface NotificationLockscreenUserManager {
String PERMISSION_SELF = "com.android.systemui.permission.SELF";
String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
= "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true,
+ prefix = {"REDACTION_TYPE_"},
+ value = {
+ REDACTION_TYPE_NONE,
+ REDACTION_TYPE_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT})
+ @interface RedactionType {}
+
+ /**
+ * Indicates that a notification requires no redaction
+ */
+ int REDACTION_TYPE_NONE = 0;
+
+ /**
+ * Indicates that a notification should have all content redacted, showing the public view.
+ * Overrides all other redaction types.
+ */
+ int REDACTION_TYPE_PUBLIC = 1;
+
+ /**
+ * Indicates that a notification should have its main content redacted, due to detected
+ * sensitive content, such as a One-Time Password
+ */
+ int REDACTION_TYPE_SENSITIVE_CONTENT = 1 << 1;
+
/**
* @param userId user Id
- * @return true if we re on a secure lock screen
+ * @return true if we're on a secure lock screen
*/
boolean isLockscreenPublicMode(int userId);
@@ -68,7 +98,13 @@ public interface NotificationLockscreenUserManager {
void updatePublicMode();
- boolean needsRedaction(NotificationEntry entry);
+ /**
+ * Determine what type of redaction is needed, if any. Returns REDACTION_TYPE_NONE if no
+ * redaction type is needed, REDACTION_TYPE_PUBLIC if private notifications are blocked, and
+ * REDACTION_TYPE_SENSITIVE_CONTENT if sensitive content is detected, and REDACTION_TYPE_PUBLIC
+ * doesn't apply.
+ */
+ @RedactionType int getRedactionType(NotificationEntry entry);
/**
* Has the given user chosen to allow their private (full) notifications to be shown even
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index a79b78f8d5f3..239257d357f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -654,8 +654,13 @@ public class NotificationLockscreenUserManagerImpl implements
}
}
- /** @return true if the entry needs redaction when on the lockscreen. */
- public boolean needsRedaction(NotificationEntry ent) {
+ /**
+ * Determine what type of redaction is needed, if any. Returns REDACTION_TYPE_NONE if no
+ * redaction type is needed, REDACTION_TYPE_PUBLIC if private notifications are blocked, and
+ * REDACTION_TYPE_SENSITIVE_CONTENT if sensitive content is detected, and REDACTION_TYPE_PUBLIC
+ * doesn't apply.
+ */
+ public @RedactionType int getRedactionType(NotificationEntry ent) {
int userId = ent.getSbn().getUserId();
boolean isCurrentUserRedactingNotifs =
@@ -675,13 +680,19 @@ public class NotificationLockscreenUserManagerImpl implements
ent.isNotificationVisibilityPrivate();
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
- if (keyguardPrivateNotifications()) {
- return !mKeyguardAllowingNotifications || isNotifSensitive
- || userForcesRedaction || (notificationRequestsRedaction && isNotifRedacted);
- } else {
- return userForcesRedaction || isNotifSensitive
- || (notificationRequestsRedaction && isNotifRedacted);
+ if (userForcesRedaction) {
+ return REDACTION_TYPE_PUBLIC;
+ }
+ if (notificationRequestsRedaction && isNotifRedacted) {
+ return REDACTION_TYPE_PUBLIC;
+ }
+ if (keyguardPrivateNotifications() && !mKeyguardAllowingNotifications) {
+ return REDACTION_TYPE_PUBLIC;
+ }
+ if (isNotifSensitive) {
+ return REDACTION_TYPE_SENSITIVE_CONTENT;
}
+ return REDACTION_TYPE_NONE;
}
private boolean packageHasVisibilityOverride(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 1db7fb429629..3408f4ffd082 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -34,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
@@ -211,17 +212,13 @@ constructor(
shadeRadius = 0f
}
- var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(shadeRadius))
var blur = shadeRadius.toInt()
-
- if (inSplitShade) {
- zoomOut = 0f
- }
-
+ val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius)
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
- blur = 0
- zoomOut = 0f
+ if (!notificationShadeBlur()) {
+ blur = 0
+ }
}
if (!blurUtils.supportsBlursOnWindows()) {
@@ -234,24 +231,43 @@ constructor(
return Pair(blur, zoomOut)
}
+ private fun blurRadiusToZoomOut(blurRadius: Float): Float {
+ var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
+ if (inSplitShade) {
+ zoomOut = 0f
+ }
+
+ if (scrimsVisible) {
+ zoomOut = 0f
+ }
+ return zoomOut
+ }
+
+ private val shouldBlurBeOpaque: Boolean
+ get() = if (notificationShadeBlur()) false else scrimsVisible && !blursDisabledForAppLaunch
+
/** Callback that updates the window blur value and is called only once per frame. */
@VisibleForTesting
val updateBlurCallback =
Choreographer.FrameCallback {
updateScheduled = false
- val (blur, zoomOut) = computeBlurAndZoomOut()
- val opaque = scrimsVisible && !blursDisabledForAppLaunch
+ val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
+ val opaque = shouldBlurBeOpaque
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
- lastAppliedBlur = blur
- wallpaperController.setNotificationShadeZoom(zoomOut)
- listeners.forEach {
- it.onWallpaperZoomOutChanged(zoomOut)
- it.onBlurRadiusChanged(blur)
- }
- notificationShadeWindowController.setBackgroundBlurRadius(blur)
+ onBlurApplied(blur, zoomOutFromShadeRadius)
}
+ private fun onBlurApplied(appliedBlurRadius: Int, zoomOutFromShadeRadius: Float) {
+ lastAppliedBlur = appliedBlurRadius
+ wallpaperController.setNotificationShadeZoom(zoomOutFromShadeRadius)
+ listeners.forEach {
+ it.onWallpaperZoomOutChanged(zoomOutFromShadeRadius)
+ it.onBlurRadiusChanged(appliedBlurRadius)
+ }
+ notificationShadeWindowController.setBackgroundBlurRadius(appliedBlurRadius)
+ }
+
/** Animate blurs when unlocking. */
private val keyguardStateCallback =
object : KeyguardStateController.Callback {
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 6f491e7beec8..85b50d3320dd 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
@@ -35,6 +35,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.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -67,9 +68,17 @@ constructor(
Flags.statusBarCallChipNotificationIcon() &&
state.notificationIconView != null
) {
+ StatusBarConnectedDisplays.assertInLegacyMode()
OngoingActivityChipModel.ChipIcon.StatusBarView(
state.notificationIconView
)
+ } else if (
+ StatusBarConnectedDisplays.isEnabled &&
+ Flags.statusBarCallChipNotificationIcon()
+ ) {
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(
+ state.notificationKey
+ )
} else {
OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
}
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 c57c807360b1..dff6f567f6c6 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
@@ -22,6 +22,7 @@ import com.android.systemui.log.core.Logger
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -56,6 +57,14 @@ constructor(
// top-level tag. It should instead be provided as the first string in each log message.
private val extraLogTag = "SingleChipInteractor[key=$key]"
+ init {
+ if (startingModel.promotedContent == null) {
+ logger.e({ "$str1: Starting model has promotedContent=null, which shouldn't happen" }) {
+ str1 = extraLogTag
+ }
+ }
+ }
+
private val _notificationModel = MutableStateFlow(startingModel)
/**
@@ -70,6 +79,14 @@ constructor(
}
return
}
+ if (model.promotedContent == null) {
+ logger.e({
+ "$str1: received model with promotedContent=null, which shouldn't happen"
+ }) {
+ str1 = extraLogTag
+ }
+ return
+ }
_notificationModel.value = model
}
@@ -98,14 +115,29 @@ constructor(
}
private fun ActiveNotificationModel.toNotificationChipModel(): NotificationChipModel? {
- val statusBarChipIconView = this.statusBarChipIconView
- if (statusBarChipIconView == null) {
- logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) {
+ val promotedContent = this.promotedContent
+ if (promotedContent == null) {
+ logger.w({
+ "$str1: Can't show chip because promotedContent=null, which shouldn't happen"
+ }) {
str1 = extraLogTag
}
return null
}
- return NotificationChipModel(key, statusBarChipIconView, whenTime)
+ val statusBarChipIconView = this.statusBarChipIconView
+ if (statusBarChipIconView == null) {
+ if (!StatusBarConnectedDisplays.isEnabled) {
+ logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) {
+ str1 = extraLogTag
+ }
+ // When the flag is disabled, we keep the old behavior of returning null.
+ // When the flag is enabled, the icon will always be null, and will later be
+ // fetched in the UI layer using the notification key.
+ return null
+ }
+ }
+
+ return NotificationChipModel(key, statusBarChipIconView, promotedContent)
}
@AssistedFactory
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 bc4241d9bca5..c6759da304bb 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
@@ -17,10 +17,11 @@
package com.android.systemui.statusbar.chips.notification.domain.model
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
/** Modeling all the data needed to render a status bar notification chip. */
data class NotificationChipModel(
val key: String,
- val statusBarChipIconView: StatusBarIconView,
- val whenTime: Long,
+ val statusBarChipIconView: StatusBarIconView?,
+ val promotedContent: PromotedNotificationContentModel,
)
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 b2f7e2fe7660..66af275bc702 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
@@ -24,10 +24,14 @@ import com.android.systemui.statusbar.chips.notification.domain.model.Notificati
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/** A view model for status bar chips for promoted ongoing notifications. */
@@ -37,22 +41,38 @@ class NotifChipsViewModel
constructor(
@Application private val applicationScope: CoroutineScope,
private val notifChipsInteractor: StatusBarNotificationChipsInteractor,
+ headsUpNotificationInteractor: HeadsUpNotificationInteractor,
) {
/**
* 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.
*/
val chips: Flow<List<OngoingActivityChipModel.Shown>> =
- notifChipsInteractor.notificationChips.map { notifications ->
- notifications.map { it.toActivityChipModel() }
+ combine(
+ notifChipsInteractor.notificationChips,
+ headsUpNotificationInteractor.statusBarHeadsUpState,
+ ) { notifications, headsUpState ->
+ notifications.map { it.toActivityChipModel(headsUpState) }
}
/** Converts the notification to the [OngoingActivityChipModel] object. */
- private fun NotificationChipModel.toActivityChipModel(): OngoingActivityChipModel.Shown {
+ private fun NotificationChipModel.toActivityChipModel(
+ headsUpState: PinnedStatus
+ ): OngoingActivityChipModel.Shown {
StatusBarNotifChips.assertInNewMode()
- val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView)
- // TODO(b/364653005): Use the notification color if applicable.
- val colors = ColorsModel.Themed
+ val icon =
+ if (this.statusBarChipIconView != null) {
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView)
+ } else {
+ StatusBarConnectedDisplays.assertInNewMode()
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(this.key)
+ }
+ val colors =
+ ColorsModel.Custom(
+ backgroundColorInt = this.promotedContent.colors.backgroundColor,
+ primaryTextColorInt = this.promotedContent.colors.primaryTextColor,
+ )
val onClickListener =
View.OnClickListener {
// The notification pipeline needs everything to run on the main thread, so keep
@@ -63,15 +83,51 @@ constructor(
)
}
}
- return OngoingActivityChipModel.Shown.ShortTimeDelta(
- icon,
- colors,
- time = this.whenTime,
- onClickListener,
- )
- // TODO(b/364653005): Use Notification.showWhen to determine if we should show the time.
- // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
- // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
- // show that app's chip.
+
+ if (headsUpState == PinnedStatus.PinnedByUser) {
+ // If the user tapped the chip to show the HUN, we want to just show the icon because
+ // the HUN will show the rest of the information.
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ }
+
+ if (this.promotedContent.shortCriticalText != null) {
+ return OngoingActivityChipModel.Shown.Text(
+ icon,
+ colors,
+ this.promotedContent.shortCriticalText,
+ onClickListener,
+ )
+ }
+
+ if (this.promotedContent.time == null) {
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ }
+ when (this.promotedContent.time.mode) {
+ PromotedNotificationContentModel.When.Mode.BasicTime -> {
+ return OngoingActivityChipModel.Shown.ShortTimeDelta(
+ icon,
+ colors,
+ time = this.promotedContent.time.time,
+ onClickListener,
+ )
+ }
+ PromotedNotificationContentModel.When.Mode.CountUp -> {
+ return OngoingActivityChipModel.Shown.Timer(
+ icon,
+ colors,
+ startTimeMs = this.promotedContent.time.time,
+ onClickListener,
+ )
+ }
+ PromotedNotificationContentModel.When.Mode.CountDown -> {
+ // TODO(b/364653005): Support CountDown.
+ return OngoingActivityChipModel.Shown.Timer(
+ icon,
+ colors,
+ startTimeMs = this.promotedContent.time.time,
+ onClickListener,
+ )
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 730784a46001..0c4c1a71ccc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -32,11 +32,13 @@ import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifCh
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.view.ChipChronometer
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
/** Binder for ongoing activity chip views. */
object OngoingActivityChipBinder {
/** Binds the given [chipModel] data to the given [chipView]. */
- fun bind(chipModel: OngoingActivityChipModel, chipView: View) {
+ fun bind(chipModel: OngoingActivityChipModel, chipView: View, iconViewStore: IconViewStore?) {
val chipContext = chipView.context
val chipDefaultIconView: ImageView =
chipView.requireViewById(R.id.ongoing_activity_chip_icon)
@@ -51,7 +53,7 @@ object OngoingActivityChipBinder {
when (chipModel) {
is OngoingActivityChipModel.Shown -> {
// Data
- setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
+ setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView, iconViewStore)
setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
chipView.setOnClickListener(chipModel.onClickListener)
updateChipPadding(
@@ -85,6 +87,7 @@ object OngoingActivityChipBinder {
chipModel: OngoingActivityChipModel.Shown,
backgroundView: ChipBackgroundContainer,
defaultIconView: ImageView,
+ iconViewStore: IconViewStore?,
) {
// Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
// it.
@@ -108,38 +111,62 @@ object OngoingActivityChipBinder {
defaultIconView.untintView()
}
is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
- // Hide the default icon since we'll show this custom icon instead.
- defaultIconView.visibility = View.GONE
-
- // Add the new custom icon:
- // 1. Set up the right visual params.
- val iconView = icon.impl
- with(iconView) {
- id = CUSTOM_ICON_VIEW_ID
- // TODO(b/354930838): Update the content description to not include "phone" and
- // maybe include the app name.
- contentDescription =
- context.resources.getString(R.string.ongoing_phone_call_content_description)
- tintView(iconTint)
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ setStatusBarIconView(defaultIconView, icon.impl, iconTint, backgroundView)
+ }
+ is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon -> {
+ StatusBarConnectedDisplays.assertInNewMode()
+ val iconView = fetchStatusBarIconView(iconViewStore, icon)
+ if (iconView == null) {
+ // This means that the notification key doesn't exist anymore.
+ return
}
+ setStatusBarIconView(defaultIconView, iconView, iconTint, backgroundView)
+ }
+ }
+ }
- // 2. If we just reinflated the view, we may need to detach the icon view from the
- // old chip before we reattach it to the new one.
- // See also: NotificationIconContainerViewBinder#bindIcons.
- val currentParent = iconView.parent as? ViewGroup
- if (currentParent != null && currentParent != backgroundView) {
- currentParent.removeView(iconView)
- currentParent.removeTransientView(iconView)
- }
+ private fun fetchStatusBarIconView(
+ iconViewStore: IconViewStore?,
+ icon: OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon,
+ ): StatusBarIconView? {
+ StatusBarConnectedDisplays.assertInNewMode()
+ if (iconViewStore == null) {
+ throw IllegalStateException("Store should always be non-null when flag is enabled.")
+ }
+ return iconViewStore.iconView(icon.notificationKey)
+ }
- // 3: Add the icon as the starting view.
- backgroundView.addView(
- iconView,
- /* index= */ 0,
- generateCustomIconLayoutParams(iconView),
- )
- }
+ private fun setStatusBarIconView(
+ defaultIconView: ImageView,
+ iconView: StatusBarIconView,
+ iconTint: Int,
+ backgroundView: ChipBackgroundContainer,
+ ) {
+ // Hide the default icon since we'll show this custom icon instead.
+ defaultIconView.visibility = View.GONE
+
+ // 1. Set up the right visual params.
+ with(iconView) {
+ id = CUSTOM_ICON_VIEW_ID
+ // TODO(b/354930838): Update the content description to not include "phone" and maybe
+ // include the app name.
+ contentDescription =
+ context.resources.getString(R.string.ongoing_phone_call_content_description)
+ tintView(iconTint)
}
+
+ // 2. If we just reinflated the view, we may need to detach the icon view from the old chip
+ // before we reattach it to the new one.
+ // See also: NotificationIconContainerViewBinder#bindIcons.
+ val currentParent = iconView.parent as? ViewGroup
+ if (currentParent != null && currentParent != backgroundView) {
+ currentParent.removeView(iconView)
+ currentParent.removeTransientView(iconView)
+ }
+
+ // 3: Add the icon as the starting view.
+ backgroundView.addView(iconView, /* index= */ 0, generateCustomIconLayoutParams(iconView))
}
private fun View.getCustomIconView(): StatusBarIconView? {
@@ -283,9 +310,13 @@ object OngoingActivityChipBinder {
private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
val sidePadding =
- context.resources.getDimensionPixelSize(
- R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
- )
+ if (StatusBarNotifChips.isEnabled) {
+ 0
+ } else {
+ context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+ )
+ }
setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
}
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 4b0fc5ab6059..efedf41e4684 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
@@ -40,21 +40,25 @@ sealed interface ColorsModel {
}
/**
- * The chip should have the given background color, and text color that matches dark/light
- * theme.
+ * The chip should have the given background color and primary text color.
+ *
+ * If [primaryTextColorInt] is null, the text color will match the current UI mode (light/dark).
*/
- data class Custom(val backgroundColorInt: Int) : ColorsModel {
+ data class Custom(val backgroundColorInt: Int, val primaryTextColorInt: Int? = null) :
+ ColorsModel {
override fun background(context: Context): ColorStateList =
ColorStateList.valueOf(backgroundColorInt)
- // TODO(b/361346412): When dark theme changes, the chip should automatically re-render with
+ // TODO(b/361346412): When UI mode changes, the chip should automatically re-render with
// the right text color. Right now, it has the right text color when the chip is first
- // created but the color doesn't update if dark theme changes.
- override fun text(context: Context) =
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.materialColorOnSurface,
- )
+ // created but the color doesn't update if UI mode changes.
+ override fun text(context: Context): Int {
+ return primaryTextColorInt
+ ?: Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.color.materialColorOnSurface,
+ )
+ }
}
/** The chip should have a red background with white text. */
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 cf07af1fc5dd..18217d786cd4 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
@@ -21,6 +21,7 @@ import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
/** Model representing the display of an ongoing activity as a chip in the status bar. */
sealed class OngoingActivityChipModel {
@@ -115,7 +116,8 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
// TODO(b/361346412): Enforce a max length requirement?
val text: String,
- ) : Shown(icon, colors, onClickListener = null) {
+ override val onClickListener: View.OnClickListener? = null,
+ ) : Shown(icon, colors, onClickListener) {
override val logName = "Shown.Text"
}
}
@@ -132,6 +134,17 @@ sealed class OngoingActivityChipModel {
"OngoingActivityChipModel.ChipIcon.StatusBarView created even though " +
"Flags.statusBarCallChipNotificationIcon is not enabled"
}
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ }
+ }
+
+ /**
+ * The icon is a custom icon, which is set on a notification, and can be looked up using the
+ * provided [notificationKey]. The icon was likely created by an external app.
+ */
+ data class StatusBarNotificationIcon(val notificationKey: String) : ChipIcon {
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
index c37b01fff578..a9c278490a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -49,7 +49,7 @@ internal class MobileState(
) : ConnectivityState() {
@JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)
@JvmField var serviceState: ServiceState? = null
@JvmField var signalStrength: SignalStrength? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
index 614f0f48d1fc..d24eddaf321f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -20,6 +20,7 @@ import android.app.StatusBarManager
import android.content.Context
import android.os.Binder
import android.os.RemoteException
+import android.view.Display
import android.view.WindowInsets
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.RegisterStatusBarResult
@@ -47,20 +48,32 @@ constructor(
override fun start() {
StatusBarConnectedDisplays.assertInNewMode()
- val result: RegisterStatusBarResult =
+ val resultPerDisplay: Map<String, RegisterStatusBarResult> =
try {
- barService.registerStatusBar(commandQueue)
+ barService.registerStatusBarForAllDisplays(commandQueue)
} catch (ex: RemoteException) {
ex.rethrowFromSystemServer()
return
}
- createNavigationBar(result)
+ resultPerDisplay[Display.DEFAULT_DISPLAY.toString()]?.let {
+ createNavigationBar(it)
+ // Set up the initial icon state
+ val numIcons: Int = it.mIcons.size
+ for (i in 0 until numIcons) {
+ commandQueue.setIcon(it.mIcons.keyAt(i), it.mIcons.valueAt(i))
+ }
+ }
+ for ((displayId, result) in resultPerDisplay.entries) {
+ initializeStatusBarForDisplay(displayId.toInt(), result)
+ }
+ }
+
+ private fun initializeStatusBarForDisplay(displayId: Int, result: RegisterStatusBarResult) {
if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
- statusBarModeRepository.defaultDisplay.showTransient()
+ statusBarModeRepository.forDisplay(displayId).showTransient()
}
- val displayId = context.display.displayId
val commandQueueCallbacks = commandQueueCallbacksLazy.get()
commandQueueCallbacks.onSystemBarAttributesChanged(
displayId,
@@ -81,12 +94,6 @@ constructor(
result.mShowImeSwitcher,
)
- // Set up the initial icon state
- val numIcons: Int = result.mIcons.size
- for (i in 0 until numIcons) {
- commandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i))
- }
-
// set the initial view visibility
val disabledFlags1 = result.mDisabledFlags1
val disabledFlags2 = result.mDisabledFlags2
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 2588c7ae2363..46c84fbc19aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
@@ -101,6 +102,19 @@ interface StatusBarModule {
@Provides
@SysUISingleton
+ @IntoMap
+ @ClassKey(OngoingCallInteractor::class)
+ fun ongoingCallInteractor(
+ interactor: OngoingCallInteractor
+ ): CoreStartable =
+ if (StatusBarChipsModernization.isEnabled) {
+ interactor
+ } else {
+ CoreStartable.NOP
+ }
+
+ @Provides
+ @SysUISingleton
fun lightBarController(store: LightBarControllerStore): LightBarController {
return store.defaultDisplay
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
index 280d66bcb827..6cf2c73a7138 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
@@ -18,9 +18,9 @@ package com.android.systemui.statusbar.data.repository
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
import com.android.systemui.CoreStartable
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
import com.android.systemui.display.data.repository.PerDisplayStore
@@ -74,7 +74,7 @@ constructor(
@SysUISingleton
class SingleDisplayStatusBarConfigurationControllerStore
@Inject
-constructor(@GlobalConfig globalConfigurationController: ConfigurationController) :
+constructor(@Main globalConfigurationController: ConfigurationController) :
StatusBarConfigurationControllerStore,
PerDisplayStore<StatusBarConfigurationController> by SingleDisplayStore(
globalConfigurationController as StatusBarConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index cc91e2dc3a25..22c37df7db7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.BoundsPair
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import dagger.assisted.Assisted
@@ -89,6 +90,9 @@ interface StatusBarModePerDisplayRepository : OnStatusBarViewInitializedListener
/** The current mode of the status bar. */
val statusBarMode: StateFlow<StatusBarMode>
+ /** Whether the status bar is forced to be visible because of an ongoing call */
+ val ongoingProcessRequiresStatusBarVisible: StateFlow<Boolean>
+
/**
* Requests for the status bar to be shown transiently.
*
@@ -110,6 +114,12 @@ interface StatusBarModePerDisplayRepository : OnStatusBarViewInitializedListener
* if needed.
*/
fun stop()
+
+ /**
+ * Called when an ongoing process needs to prevent the status bar from being hidden in any
+ * state.
+ */
+ fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean)
}
class StatusBarModePerDisplayRepositoryImpl
@@ -195,6 +205,16 @@ constructor(
statusBarBoundsProvider.addChangeListener(listener)
}
+ private val _ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
+ override val ongoingProcessRequiresStatusBarVisible =
+ _ongoingProcessRequiresStatusBarVisible.asStateFlow()
+
+ override fun setOngoingProcessRequiresStatusBarVisible(
+ requiredVisible: Boolean
+ ) {
+ _ongoingProcessRequiresStatusBarVisible.value = requiredVisible
+ }
+
override val isInFullscreenMode: StateFlow<Boolean> =
_originalStatusBarAttributes
.map { params ->
@@ -235,16 +255,28 @@ constructor(
isTransientShown,
isInFullscreenMode,
ongoingCallRepository.ongoingCallState,
- ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
+ _ongoingProcessRequiresStatusBarVisible,
+ ) {
+ modifiedAttributes,
+ isTransientShown,
+ isInFullscreenMode,
+ ongoingCallStateLegacy,
+ ongoingProcessRequiresStatusBarVisible ->
if (modifiedAttributes == null) {
null
} else {
+ val hasOngoingCall =
+ if (StatusBarChipsModernization.isEnabled) {
+ ongoingProcessRequiresStatusBarVisible
+ } else {
+ ongoingCallStateLegacy is OngoingCallModel.InCall
+ }
val statusBarMode =
toBarMode(
modifiedAttributes.appearance,
isTransientShown,
isInFullscreenMode,
- hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
+ hasOngoingCall,
)
StatusBarAppearance(
statusBarMode,
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 6b84b6d07702..c38b84b710bc 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
@@ -666,7 +666,16 @@ public final class NotificationEntry extends ListEntry {
}
public boolean isRowPinned() {
- return row != null && row.isPinned();
+ return getPinnedStatus().isPinned();
+ }
+
+ /** Returns this notification's current pinned status. */
+ public PinnedStatus getPinnedStatus() {
+ if (row != null) {
+ return row.getPinnedStatus();
+ } else {
+ return PinnedStatus.NotPinned;
+ }
}
/**
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 0269b16d4490..eb6ec9f59a3e 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
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
import com.android.systemui.statusbar.notification.logKey
@@ -145,6 +146,7 @@ constructor(
// heads-up is considered to be the top notification.
shouldHeadsUpEver = true,
shouldHeadsUpAgain = true,
+ isPinnedByUser = true,
isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key),
isBinding = isEntryBinding(entry),
)
@@ -155,8 +157,8 @@ constructor(
}
}
- private fun onHeadsUpViewBound(entry: NotificationEntry) {
- mHeadsUpManager.showNotification(entry)
+ private fun onHeadsUpViewBound(entry: NotificationEntry, isPinnedByUser: Boolean) {
+ mHeadsUpManager.showNotification(entry, isPinnedByUser)
mEntriesBindingUntil.remove(entry.key)
}
@@ -424,6 +426,7 @@ constructor(
private fun handlePostedEntry(posted: PostedEntry, hunMutator: HunMutator, scenario: String) {
mLogger.logPostedEntryWillEvaluate(posted, scenario)
+
if (posted.wasAdded) {
if (posted.shouldHeadsUpEver) {
bindForAsyncHeadsUp(posted)
@@ -437,7 +440,17 @@ constructor(
// If showing heads up, we need to post an update. Otherwise we're still
// binding, and we can just let that finish.
if (posted.isHeadsUpEntry) {
- hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+ val pinnedStatus =
+ if (posted.shouldHeadsUpAgain) {
+ if (StatusBarNotifChips.isEnabled && posted.isPinnedByUser) {
+ PinnedStatus.PinnedByUser
+ } else {
+ PinnedStatus.PinnedBySystem
+ }
+ } else {
+ PinnedStatus.NotPinned
+ }
+ hunMutator.updateNotification(posted.key, pinnedStatus)
}
} else {
if (posted.isHeadsUpEntry) {
@@ -461,10 +474,11 @@ constructor(
}
private fun bindForAsyncHeadsUp(posted: PostedEntry) {
+ val isPinnedByUser = StatusBarNotifChips.isEnabled && posted.isPinnedByUser
// TODO: Add a guarantee to bindHeadsUpView of some kind of callback if the bind is
// cancelled so that we don't need to have this sad timeout hack.
mEntriesBindingUntil[posted.key] = mNow + BIND_TIMEOUT
- mHeadsUpViewBinder.bindHeadsUpView(posted.entry, this::onHeadsUpViewBound)
+ mHeadsUpViewBinder.bindHeadsUpView(posted.entry, isPinnedByUser, this::onHeadsUpViewBound)
}
private val mNotifCollectionListener =
@@ -906,6 +920,7 @@ constructor(
var wasUpdated: Boolean,
var shouldHeadsUpEver: Boolean,
var shouldHeadsUpAgain: Boolean,
+ var isPinnedByUser: Boolean = false,
var isHeadsUpEntry: Boolean,
var isBinding: Boolean,
) {
@@ -943,7 +958,7 @@ private fun <R> HeadsUpManager.modifyHuns(block: (HunMutator) -> R): R {
/** Mutates the HeadsUp state of notifications. */
private interface HunMutator {
- fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+ fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus)
fun removeNotification(key: String, releaseImmediately: Boolean)
}
@@ -955,8 +970,8 @@ private interface HunMutator {
private class HunMutatorImpl(private val headsUpManager: HeadsUpManager) : HunMutator {
private val deferred = mutableListOf<Pair<String, Boolean>>()
- override fun updateNotification(key: String, shouldHeadsUpAgain: Boolean) {
- headsUpManager.updateNotification(key, shouldHeadsUpAgain)
+ override fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus) {
+ headsUpManager.updateNotification(key, requestedPinnedStatus)
}
override fun removeNotification(key: String, releaseImmediately: Boolean) {
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 f75163d2662b..2ecce1f02ef6 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
@@ -416,7 +416,7 @@ public class PreparationCoordinator implements Coordinator {
/* showSnooze = */ adjustment.isSnoozeEnabled(),
/* isChildInGroup = */ adjustment.isChildInGroup(),
/* isGroupSummary = */ adjustment.isGroupSummary(),
- /* needsRedaction = */ adjustment.getNeedsRedaction()
+ /* needsRedaction = */ adjustment.getRedactionType()
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index ef7b1c3d562e..1875e7e46693 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -14,10 +14,13 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.server.notification.Flags.screenshareNotificationHiding
import com.android.systemui.dagger.qualifiers.Application
@@ -27,6 +30,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -44,9 +48,9 @@ import dagger.Binds
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
-import com.android.app.tracing.coroutines.launchTraced as launch
@Module(includes = [PrivateSensitiveContentCoordinatorModule::class])
interface SensitiveContentCoordinatorModule
@@ -80,6 +84,7 @@ constructor(
DynamicPrivacyController.Listener,
OnBeforeRenderListListener {
private var inTransitionFromLockedToGone = false
+ private var canSwipeToEnter = false
private val onSensitiveStateChanged = Runnable() { invalidateList("onSensitiveStateChanged") }
@@ -98,7 +103,9 @@ constructor(
}
override fun attach(pipeline: NotifPipeline) {
- dynamicPrivacyController.addListener(this)
+ if (!SceneContainerFlag.isEnabled) {
+ dynamicPrivacyController.addListener(this)
+ }
if (screenshareNotificationHiding()) {
sensitiveNotificationProtectionController.registerSensitiveStateListener(
onSensitiveStateChanged
@@ -128,6 +135,15 @@ constructor(
invalidateList("inTransitionFromLockedToGoneChanged")
}
}
+ scope.launch {
+ deviceEntryInteractor.canSwipeToEnter.collect {
+ val canSwipeToEnter = it ?: false
+ if (this@SensitiveContentCoordinatorImpl.canSwipeToEnter != canSwipeToEnter) {
+ this@SensitiveContentCoordinatorImpl.canSwipeToEnter = canSwipeToEnter
+ invalidateList("canSwipeToEnterChanged")
+ }
+ }
+ }
}
}
@@ -168,7 +184,9 @@ constructor(
(devicePublic &&
!lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)) ||
isSensitiveContentProtectionActive
- val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked
+ val dynamicallyUnlocked =
+ if (SceneContainerFlag.isEnabled) canSwipeToEnter
+ else dynamicPrivacyController.isDynamicallyUnlocked
for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) {
val notifUserId = entry.sbn.user.identifier
val userLockscreen =
@@ -194,7 +212,8 @@ constructor(
screenshareNotificationHiding() &&
sensitiveNotificationProtectionController.shouldProtectNotification(entry)
- val needsRedaction = lockscreenUserManager.needsRedaction(entry)
+ val needsRedaction =
+ lockscreenUserManager.getRedactionType(entry) != REDACTION_TYPE_NONE
val isSensitive = userPublic && needsRedaction
entry.setSensitive(isSensitive || shouldProtectNotification, deviceSensitive)
if (screenshareNotificationHiding()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index ff72888a5c26..ff9d533a09d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.inflation
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.render.NotifViewController
@@ -61,6 +62,6 @@ interface NotifInflater {
val showSnooze: Boolean,
val isChildInGroup: Boolean = false,
val isGroupSummary: Boolean = false,
- val needsRedaction: Boolean,
+ @RedactionType val redactionType: Int,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index e70fb6b0fdf3..331ef1c01596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -20,22 +20,24 @@ import android.app.Notification
import android.app.RemoteInput
import android.graphics.drawable.Icon
import android.text.TextUtils
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation
/**
* An immutable object which contains minimal state extracted from an entry that represents state
- * which can change without a direct app update (e.g. with a ranking update).
- * Diffing two entries determines if view re-inflation is needed.
+ * which can change without a direct app update (e.g. with a ranking update). Diffing two entries
+ * determines if view re-inflation is needed.
*/
-class NotifUiAdjustment internal constructor(
+class NotifUiAdjustment
+internal constructor(
val key: String,
val smartActions: List<Notification.Action>,
val smartReplies: List<CharSequence>,
val isConversation: Boolean,
val isSnoozeEnabled: Boolean,
val isMinimized: Boolean,
- val needsRedaction: Boolean,
+ @RedactionType val redactionType: Int,
val isChildInGroup: Boolean,
val isGroupSummary: Boolean,
) {
@@ -43,65 +45,72 @@ class NotifUiAdjustment internal constructor(
@JvmStatic
fun needReinflate(
oldAdjustment: NotifUiAdjustment,
- newAdjustment: NotifUiAdjustment
- ): Boolean = when {
- oldAdjustment === newAdjustment -> false
- oldAdjustment.isConversation != newAdjustment.isConversation -> true
- oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
- oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
- oldAdjustment.needsRedaction != newAdjustment.needsRedaction -> true
- areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
- newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
- AsyncHybridViewInflation.isEnabled &&
- !oldAdjustment.isChildInGroup && newAdjustment.isChildInGroup -> true
- AsyncGroupHeaderViewInflation.isEnabled &&
- !oldAdjustment.isGroupSummary && newAdjustment.isGroupSummary -> true
- else -> false
- }
+ newAdjustment: NotifUiAdjustment,
+ ): Boolean =
+ when {
+ oldAdjustment === newAdjustment -> false
+ oldAdjustment.isConversation != newAdjustment.isConversation -> true
+ oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
+ oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
+ oldAdjustment.redactionType != newAdjustment.redactionType -> true
+ areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
+ newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
+ AsyncHybridViewInflation.isEnabled &&
+ !oldAdjustment.isChildInGroup &&
+ newAdjustment.isChildInGroup -> true
+ AsyncGroupHeaderViewInflation.isEnabled &&
+ !oldAdjustment.isGroupSummary &&
+ newAdjustment.isGroupSummary -> true
+ else -> false
+ }
private fun areDifferent(
first: List<Notification.Action>,
- second: List<Notification.Action>
- ): Boolean = when {
- first === second -> false
- first.size != second.size -> true
- else -> first.asSequence().zip(second.asSequence()).any {
- (!TextUtils.equals(it.first.title, it.second.title)) ||
- (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
- (it.first.actionIntent != it.second.actionIntent) ||
- (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+ second: List<Notification.Action>,
+ ): Boolean =
+ when {
+ first === second -> false
+ first.size != second.size -> true
+ else ->
+ first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.title, it.second.title)) ||
+ (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+ (it.first.actionIntent != it.second.actionIntent) ||
+ (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+ }
}
- }
- private fun areDifferent(first: Icon?, second: Icon?): Boolean = when {
- first === second -> false
- first == null || second == null -> true
- else -> !first.sameAs(second)
- }
+ private fun areDifferent(first: Icon?, second: Icon?): Boolean =
+ when {
+ first === second -> false
+ first == null || second == null -> true
+ else -> !first.sameAs(second)
+ }
- private fun areDifferent(
- first: Array<RemoteInput>?,
- second: Array<RemoteInput>?
- ): Boolean = when {
- first === second -> false
- first == null || second == null -> true
- first.size != second.size -> true
- else -> first.asSequence().zip(second.asSequence()).any {
- (!TextUtils.equals(it.first.label, it.second.label)) ||
- (areDifferent(it.first.choices, it.second.choices))
+ private fun areDifferent(first: Array<RemoteInput>?, second: Array<RemoteInput>?): Boolean =
+ when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else ->
+ first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.label, it.second.label)) ||
+ (areDifferent(it.first.choices, it.second.choices))
+ }
}
- }
private fun areDifferent(
first: Array<CharSequence>?,
- second: Array<CharSequence>?
- ): Boolean = when {
- first === second -> false
- first == null || second == null -> true
- first.size != second.size -> true
- else -> first.asSequence().zip(second.asSequence()).any {
- !TextUtils.equals(it.first, it.second)
+ second: Array<CharSequence>?,
+ ): Boolean =
+ when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else ->
+ first.asSequence().zip(second.asSequence()).any {
+ !TextUtils.equals(it.first, it.second)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 4c82bc193c77..97e55c19d2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
@@ -79,7 +80,7 @@ constructor(
secureSettings.registerContentObserverForUserSync(
SHOW_NOTIFICATION_SNOOZE,
settingsObserver,
- UserHandle.USER_ALL
+ UserHandle.USER_ALL,
)
}
dirtyListeners.addIfAbsent(listener)
@@ -140,10 +141,15 @@ constructor(
isConversation = entry.ranking.isConversation,
isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
isMinimized = isEntryMinimized(entry),
- needsRedaction =
- lockscreenUserManager.needsRedaction(entry) ||
- (screenshareNotificationHiding() &&
- sensitiveNotifProtectionController.shouldProtectNotification(entry)),
+ redactionType =
+ if (
+ screenshareNotificationHiding() &&
+ sensitiveNotifProtectionController.shouldProtectNotification(entry)
+ ) {
+ REDACTION_TYPE_PUBLIC
+ } else {
+ lockscreenUserManager.getRedactionType(entry)
+ },
isChildInGroup = entry.hasEverBeenGroupChild(),
isGroupSummary = entry.hasEverBeenGroupSummary(),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e6d22b07f3ab..80e8f55b897a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.server.notification.Flags.screenshareNotificationHiding;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -256,9 +256,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseMinimized(isMinimized);
- boolean needsRedaction = screenshareNotificationHiding()
- ? inflaterParams.getNeedsRedaction()
- : mNotificationLockscreenUserManager.needsRedaction(entry);
+ // TODO b/358403414: use the different types of redaction
+ boolean needsRedaction = inflaterParams.getRedactionType() != REDACTION_TYPE_NONE;
if (needsRedaction) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 64e78e4fbe48..75c7d2d5be98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -97,19 +98,22 @@ constructor(
}
}
- /** Are there any pinned heads up rows to display? */
- val hasPinnedRows: Flow<Boolean> =
+ /** What [PinnedStatus] does the top row have? */
+ private val topPinnedStatus: Flow<PinnedStatus> =
headsUpRepository.activeHeadsUpRows.flatMapLatest { rows ->
if (rows.isNotEmpty()) {
combine(rows.map { it.pinnedStatus }) { pinnedStatus ->
- pinnedStatus.any { it.isPinned }
+ pinnedStatus.firstOrNull { it.isPinned } ?: PinnedStatus.NotPinned
}
} else {
// if the set is empty, there are no flows to combine
- flowOf(false)
+ flowOf(PinnedStatus.NotPinned)
}
}
+ /** Are there any pinned heads up rows to display? */
+ val hasPinnedRows: Flow<Boolean> = topPinnedStatus.map { it.isPinned }
+
val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
@@ -138,9 +142,14 @@ constructor(
}
}
- val showHeadsUpStatusBar =
- combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp ->
- hasPinnedRows && canShowHeadsUp
+ /** Emits the pinned notification state as it relates to the status bar. */
+ val statusBarHeadsUpState: Flow<PinnedStatus> =
+ combine(topPinnedStatus, canShowHeadsUp) { topPinnedStatus, canShowHeadsUp ->
+ if (canShowHeadsUp) {
+ topPinnedStatus
+ } else {
+ PinnedStatus.NotPinned
+ }
}
fun headsUpRow(key: HeadsUpRowKey): HeadsUpRowInteractor =
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 a0515ca92cdd..d25889820629 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
@@ -39,7 +39,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
-import com.android.settingslib.Utils;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
@@ -457,8 +456,8 @@ public class FooterView extends StackScrollerDecorView {
*/
public void updateColors() {
Resources.Theme theme = mContext.getTheme();
- final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorOnSurface);
+ final @ColorInt int onSurface = mContext.getColor(
+ com.android.internal.R.color.materialColorOnSurface);
// Same resource, separate drawables to prevent touch effects from showing on the wrong
// button.
final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
@@ -467,8 +466,8 @@ public class FooterView extends StackScrollerDecorView {
? theme.getDrawable(R.drawable.notif_footer_btn_background) : null;
final @ColorInt int scHigh;
if (!notificationFooterBackgroundTintOptimization()) {
- scHigh = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorSurfaceContainerHigh);
+ scHigh = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
if (scHigh != 0) {
final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
clearAllBg.setColorFilter(bgColorFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
index 424a3c5e6af9..95234dacc899 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
@@ -88,6 +88,12 @@ interface HeadsUpManager : Dumpable {
/** Returns whether there are any pinned Heads Up Notifications or not. */
fun hasPinnedHeadsUp(): Boolean
+ /**
+ * Returns the status of the top Heads Up Notification, or returns [PinnedStatus.NotPinned] if
+ * there is no pinned HUN.
+ */
+ fun pinnedHeadsUpStatus(): PinnedStatus
+
/** Returns whether or not the given notification is managed by this manager. */
fun isHeadsUpEntry(key: String): Boolean
@@ -204,8 +210,10 @@ interface HeadsUpManager : Dumpable {
* the notification to be managed.
*
* @param entry entry to show
+ * @param isPinnedByUser true if the notification was pinned by the user and false if the
+ * notification was pinned by the system.
*/
- fun showNotification(entry: NotificationEntry)
+ fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean = false)
fun snooze()
@@ -216,7 +224,15 @@ interface HeadsUpManager : Dumpable {
*/
fun unpinAll(userUnPinned: Boolean)
- fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+ /**
+ * Called when the notification state has been updated.
+ *
+ * @param key the key of the entry that was updated
+ * @param requestedPinnedStatus whether and how the notification should be pinned. If equal to
+ * [PinnedStatus.NotPinned], the notification won't show again. Otherwise, the notification
+ * should show again and will force reevaluation of removal time.
+ */
+ fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus)
fun onEntryAnimatingAwayEnded(entry: NotificationEntry)
}
@@ -262,6 +278,8 @@ class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
override fun hasPinnedHeadsUp() = false
+ override fun pinnedHeadsUpStatus() = PinnedStatus.NotPinned
+
override fun isHeadsUpEntry(key: String) = false
override fun isHeadsUpAnimatingAwayValue() = false
@@ -306,13 +324,13 @@ class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
override fun shouldSwallowClick(key: String): Boolean = false
- override fun showNotification(entry: NotificationEntry) {}
+ override fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean) {}
override fun snooze() {}
override fun unpinAll(userUnPinned: Boolean) {}
- override fun updateNotification(key: String, alert: Boolean) {}
+ override fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus) {}
override fun onEntryAnimatingAwayEnded(entry: NotificationEntry) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 0b188afa1c35..6756077e5444 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -45,7 +45,9 @@ 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.statusbar.StatusBarState;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -64,6 +66,11 @@ import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
@@ -77,11 +84,6 @@ import java.util.stream.Stream;
import javax.inject.Inject;
-import kotlinx.coroutines.flow.Flow;
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
@@ -92,14 +94,15 @@ public class HeadsUpManagerImpl
private static final String TAG = "BaseHeadsUpManager";
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
private static final String REASON_REORDER_ALLOWED = "mOnReorderingAllowedListener";
- protected final ListenerSet<OnHeadsUpChangedListener> mListeners = new ListenerSet<>();
+ private final ListenerSet<OnHeadsUpChangedListener> mListeners = new ListenerSet<>();
- protected final Context mContext;
+ private final Context mContext;
- protected int mTouchAcceptanceDelay;
- protected int mSnoozeLengthMs;
- protected boolean mHasPinnedNotification;
- protected int mUser;
+ private final int mTouchAcceptanceDelay;
+ private int mSnoozeLengthMs;
+ private boolean mHasPinnedNotification;
+ private PinnedStatus mPinnedNotificationStatus = PinnedStatus.NotPinned;
+ private int mUser;
private final ArrayMap<String, Long> mSnoozedPackages;
private final AccessibilityManagerWrapper mAccessibilityMgr;
@@ -111,13 +114,14 @@ public class HeadsUpManagerImpl
private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
private final VisualStabilityProvider mVisualStabilityProvider;
- protected final SystemClock mSystemClock;
- protected final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>();
- protected final HeadsUpManagerLogger mLogger;
- protected int mMinimumDisplayTime;
- protected int mStickyForSomeTimeAutoDismissTime;
- protected int mAutoDismissTime;
- protected DelayableExecutor mExecutor;
+ private final SystemClock mSystemClock;
+ @VisibleForTesting
+ final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>();
+ private final HeadsUpManagerLogger mLogger;
+ private final int mMinimumDisplayTime;
+ private final int mStickyForSomeTimeAutoDismissTime;
+ private final int mAutoDismissTime;
+ private final DelayableExecutor mExecutor;
private final int mExtensionTime;
@@ -131,7 +135,7 @@ public class HeadsUpManagerImpl
private final HashSet<String> mSwipedOutKeys = new HashSet<>();
private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
@VisibleForTesting
- public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
+ final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
= new ArraySet<>();
private boolean mReleaseOnExpandFinish;
@@ -183,7 +187,7 @@ public class HeadsUpManagerImpl
@Inject
public HeadsUpManagerImpl(
- @NonNull final Context context,
+ @NonNull @ShadeDisplayAware final Context context,
HeadsUpManagerLogger logger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
@@ -303,28 +307,28 @@ public class HeadsUpManagerImpl
+ resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
}
- /**
- * Called when posting a new notification that should appear on screen.
- * Adds the notification to be managed.
- * @param entry entry to show
- */
@Override
- public void showNotification(@NonNull NotificationEntry entry) {
+ public void showNotification(
+ @NonNull NotificationEntry entry, boolean isPinnedByUser) {
HeadsUpEntry headsUpEntry = createHeadsUpEntry(entry);
- mLogger.logShowNotificationRequest(entry);
+ mLogger.logShowNotificationRequest(entry, isPinnedByUser);
Runnable runnable = () -> {
- mLogger.logShowNotification(entry);
+ mLogger.logShowNotification(entry, isPinnedByUser);
// Add new entry and begin managing it
mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
- onEntryAdded(headsUpEntry);
+ PinnedStatus requestedPinnedStatus =
+ isPinnedByUser
+ ? PinnedStatus.PinnedByUser
+ : PinnedStatus.PinnedBySystem;
+ onEntryAdded(headsUpEntry, requestedPinnedStatus);
// TODO(b/328390331) move accessibility events to the view layer
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.setIsHeadsUpEntry(true);
- updateNotificationInternal(entry.getKey(), true /* shouldHeadsUpAgain */);
+ updateNotificationInternal(entry.getKey(), requestedPinnedStatus);
entry.setInterruption();
};
mAvalancheController.update(headsUpEntry, runnable, "showNotification");
@@ -375,25 +379,20 @@ public class HeadsUpManagerImpl
return false;
}
- /**
- * Called when the notification state has been updated.
- * @param key the key of the entry that was updated
- * @param shouldHeadsUpAgain whether the notification should show again and force reevaluation
- * of removal time
- */
- public void updateNotification(@NonNull String key, boolean shouldHeadsUpAgain) {
+ @Override
+ public void updateNotification(
+ @NonNull String key, @NonNull PinnedStatus requestedPinnedStatus) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
- mLogger.logUpdateNotificationRequest(key, shouldHeadsUpAgain, headsUpEntry != null);
+ mLogger.logUpdateNotificationRequest(key, requestedPinnedStatus, headsUpEntry != null);
- Runnable runnable = () -> {
- updateNotificationInternal(key, shouldHeadsUpAgain);
- };
+ Runnable runnable = () -> updateNotificationInternal(key, requestedPinnedStatus);
mAvalancheController.update(headsUpEntry, runnable, "updateNotification");
}
- private void updateNotificationInternal(@NonNull String key, boolean shouldHeadsUpAgain) {
+ private void updateNotificationInternal(
+ @NonNull String key, PinnedStatus requestedPinnedStatus) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
- mLogger.logUpdateNotification(key, shouldHeadsUpAgain, headsUpEntry != null);
+ mLogger.logUpdateNotification(key, requestedPinnedStatus, headsUpEntry != null);
if (headsUpEntry == null) {
// the entry was released before this update (i.e by a listener) This can happen
// with the groupmanager
@@ -404,14 +403,11 @@ public class HeadsUpManagerImpl
headsUpEntry.mEntry.sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
- if (shouldHeadsUpAgain) {
+ if (requestedPinnedStatus.isPinned()) {
headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification");
- PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(headsUpEntry.mEntry)
- ? PinnedStatus.PinnedBySystem
- : PinnedStatus.NotPinned;
- if (headsUpEntry != null) {
- setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal");
- }
+ PinnedStatus pinnedStatus =
+ getNewPinnedStatusForEntry(headsUpEntry, requestedPinnedStatus);
+ setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal");
}
}
@@ -517,8 +513,7 @@ public class HeadsUpManagerImpl
}
/**
- * @param key
- * @return When a HUN entry should be removed in milliseconds from now
+ * @return When a HUN entry with the given key should be removed in milliseconds from now
*/
@Override
public long getEarliestRemovalTime(String key) {
@@ -530,7 +525,10 @@ public class HeadsUpManagerImpl
}
@VisibleForTesting
- protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
+ boolean shouldHeadsUpBecomePinned(@Nullable NotificationEntry entry) {
+ if (entry == null) {
+ return false;
+ }
boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded;
if (SceneContainerFlag.isEnabled()) {
pin |= mIsQsExpanded;
@@ -551,24 +549,18 @@ public class HeadsUpManagerImpl
return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned;
}
- protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
- if (entry == null) {
- return false;
- }
- if (entry.getSbn() == null) {
- return false;
- }
+ private boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
if (entry.getSbn().getNotification() == null) {
return false;
}
return entry.getSbn().getNotification().fullScreenIntent != null;
}
- protected void setEntryPinned(
+ private void setEntryPinned(
@NonNull HeadsUpManagerImpl.HeadsUpEntry headsUpEntry, PinnedStatus pinnedStatus,
String reason) {
- mLogger.logSetEntryPinned(headsUpEntry.mEntry, pinnedStatus, reason);
- NotificationEntry entry = headsUpEntry.mEntry;
+ NotificationEntry entry = headsUpEntry.requireEntry();
+ mLogger.logSetEntryPinned(entry, pinnedStatus, reason);
boolean isPinned = pinnedStatus.isPinned();
if (!isPinned) {
headsUpEntry.mWasUnpinned = true;
@@ -576,7 +568,7 @@ public class HeadsUpManagerImpl
if (headsUpEntry.getPinnedStatus().getValue() != pinnedStatus) {
headsUpEntry.setRowPinnedStatus(pinnedStatus);
updatePinnedMode();
- if (isPinned && entry.getSbn() != null) {
+ if (isPinned) {
mUiEventLogger.logWithInstanceId(
NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
@@ -596,27 +588,48 @@ public class HeadsUpManagerImpl
* Manager-specific logic that should occur when an entry is added.
* @param headsUpEntry entry added
*/
- protected void onEntryAdded(HeadsUpEntry headsUpEntry) {
- NotificationEntry entry = headsUpEntry.mEntry;
+ @VisibleForTesting
+ void onEntryAdded(HeadsUpEntry headsUpEntry, PinnedStatus requestedPinnedStatus) {
+ NotificationEntry entry = headsUpEntry.requireEntry();
entry.setHeadsUp(true);
- final PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(entry)
- ? PinnedStatus.PinnedBySystem
- : PinnedStatus.NotPinned;
+ PinnedStatus pinnedStatus = getNewPinnedStatusForEntry(headsUpEntry, requestedPinnedStatus);
setEntryPinned(headsUpEntry, pinnedStatus, "onEntryAdded");
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
for (OnHeadsUpChangedListener listener : mListeners) {
+ // TODO(b/382509804): It's odd that if pinnedStatus == PinnedStatus.NotPinned, then we
+ // still send isHeadsUp=true to listeners. Is this causing bugs?
listener.onHeadsUpStateChanged(entry, true);
}
updateTopHeadsUpFlow();
updateHeadsUpFlow();
}
+ private PinnedStatus getNewPinnedStatusForEntry(
+ HeadsUpEntry headsUpEntry, PinnedStatus requestedPinnedStatus) {
+ NotificationEntry entry = headsUpEntry.mEntry;
+ if (entry == null) {
+ return PinnedStatus.NotPinned;
+ }
+ boolean shouldBecomePinned = shouldHeadsUpBecomePinned(entry);
+ if (!shouldBecomePinned) {
+ return PinnedStatus.NotPinned;
+ }
+
+ if (!StatusBarNotifChips.isEnabled()
+ && requestedPinnedStatus == PinnedStatus.PinnedByUser) {
+ Log.wtf(TAG, "PinnedStatus.PinnedByUser not allowed if StatusBarNotifChips flag off");
+ return PinnedStatus.NotPinned;
+ }
+
+ return requestedPinnedStatus;
+ }
+
/**
* Remove a notification from the alerting entries.
* @param key key of notification to remove
*/
- protected final void removeEntry(@NonNull String key, String reason) {
+ private void removeEntry(@NonNull String key, String reason) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
boolean isWaiting;
if (headsUpEntry == null) {
@@ -633,10 +646,10 @@ public class HeadsUpManagerImpl
if (finalHeadsUpEntry == null) {
return;
}
- NotificationEntry entry = finalHeadsUpEntry.mEntry;
+ NotificationEntry entry = finalHeadsUpEntry.requireEntry();
// If the notification is animating, we will remove it at the end of the animation.
- if (entry != null && entry.isExpandAnimationRunning()) {
+ if (entry.isExpandAnimationRunning()) {
return;
}
entry.demoteStickyHun();
@@ -658,8 +671,9 @@ public class HeadsUpManagerImpl
* @param headsUpEntry entry removed
* @param reason why onEntryRemoved was called
*/
- protected void onEntryRemoved(HeadsUpEntry headsUpEntry, String reason) {
- NotificationEntry entry = headsUpEntry.mEntry;
+ @VisibleForTesting
+ void onEntryRemoved(@NonNull HeadsUpEntry headsUpEntry, String reason) {
+ NotificationEntry entry = headsUpEntry.requireEntry();
entry.setHeadsUp(false);
setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "onEntryRemoved");
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
@@ -681,15 +695,14 @@ public class HeadsUpManagerImpl
// mEntriesToRemoveWhenReorderingAllowed, we should not remove from this list (and cause
// ArrayIndexOutOfBoundsException). We don't need to in this case anyway, because we
// clear mEntriesToRemoveWhenReorderingAllowed after removing these entries.
- if (!reason.equals(REASON_REORDER_ALLOWED)
- && mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)) {
+ if (!reason.equals(REASON_REORDER_ALLOWED)) {
mEntriesToRemoveWhenReorderingAllowed.remove(notifEntry);
}
}
}
private void updateTopHeadsUpFlow() {
- mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry());
+ mTopHeadsUpRow.setValue(getTopHeadsUpEntry());
}
private void updateHeadsUpFlow() {
@@ -734,23 +747,13 @@ public class HeadsUpManagerImpl
}
}
- /**
- * Manager-specific logic, that should occur, when the entry is updated, and its posted time has
- * changed.
- *
- * @param headsUpEntry entry updated
- */
- protected void onEntryUpdated(HeadsUpEntry headsUpEntry) {
- // no need to update the list here
- updateTopHeadsUpFlow();
- }
-
- protected void updatePinnedMode() {
+ private void updatePinnedMode() {
boolean hasPinnedNotification = hasPinnedNotificationInternal();
+ mPinnedNotificationStatus = pinnedNotificationStatusInternal();
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
- mLogger.logUpdatePinnedMode(hasPinnedNotification);
+ mLogger.logUpdatePinnedMode(hasPinnedNotification, mPinnedNotificationStatus);
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
@@ -786,7 +789,7 @@ public class HeadsUpManagerImpl
keySet.addAll(mAvalancheController.getWaitingKeys());
for (String key : keySet) {
HeadsUpEntry entry = getHeadsUpEntry(key);
- if (entry.mEntry == null) {
+ if (entry == null || entry.mEntry == null) {
continue;
}
String packageName = entry.mEntry.getSbn().getPackageName();
@@ -808,7 +811,8 @@ public class HeadsUpManagerImpl
}
@Nullable
- protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
+ @VisibleForTesting
+ HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
if (mHeadsUpEntryMap.containsKey(key)) {
return mHeadsUpEntryMap.get(key);
}
@@ -825,7 +829,7 @@ public class HeadsUpManagerImpl
}
@Nullable
- protected HeadsUpEntry getTopHeadsUpEntry() {
+ private HeadsUpEntry getTopHeadsUpEntry() {
if (mHeadsUpEntryMap.isEmpty()) {
return null;
}
@@ -921,7 +925,7 @@ public class HeadsUpManagerImpl
dumpInternal(pw, args);
}
- protected void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
+ private void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
pw.print(" now="); pw.println(mSystemClock.elapsedRealtime());
@@ -941,23 +945,40 @@ public class HeadsUpManagerImpl
pw.println(mTouchableRegion);
}
- /**
- * Returns if there are any pinned Heads Up Notifications or not.
- */
+ @Override
public boolean hasPinnedHeadsUp() {
return mHasPinnedNotification;
}
+ @Override
+ @NonNull
+ public PinnedStatus pinnedHeadsUpStatus() {
+ if (!StatusBarNotifChips.isEnabled()) {
+ return mHasPinnedNotification ? PinnedStatus.PinnedBySystem : PinnedStatus.NotPinned;
+ }
+ return mPinnedNotificationStatus;
+ }
+
private boolean hasPinnedNotificationInternal() {
for (String key : mHeadsUpEntryMap.keySet()) {
HeadsUpEntry entry = getHeadsUpEntry(key);
- if (entry.mEntry != null && entry.mEntry.isRowPinned()) {
+ if (entry != null && entry.mEntry != null && entry.mEntry.isRowPinned()) {
return true;
}
}
return false;
}
+ private PinnedStatus pinnedNotificationStatusInternal() {
+ for (String key : mHeadsUpEntryMap.keySet()) {
+ HeadsUpEntry entry = getHeadsUpEntry(key);
+ if (entry.mEntry != null && entry.mEntry.isRowPinned()) {
+ return entry.mEntry.getPinnedStatus();
+ }
+ }
+ return PinnedStatus.NotPinned;
+ }
+
/**
* Unpins all pinned Heads Up Notifications.
* @param userUnPinned The unpinned action is trigger by user real operation.
@@ -966,6 +987,10 @@ public class HeadsUpManagerImpl
public void unpinAll(boolean userUnPinned) {
for (String key : mHeadsUpEntryMap.keySet()) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (headsUpEntry == null) {
+ Log.wtf(TAG, "Couldn't find entry " + key + " in unpinAll");
+ continue;
+ }
mLogger.logUnpinEntryRequest(key);
Runnable runnable = () -> {
mLogger.logUnpinEntry(key);
@@ -976,10 +1001,10 @@ public class HeadsUpManagerImpl
// when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
// on the screen.
- if (userUnPinned && headsUpEntry.mEntry != null) {
- if (headsUpEntry.mEntry != null && headsUpEntry.mEntry.mustStayOnScreen()) {
- headsUpEntry.mEntry.setHeadsUpIsVisible();
- }
+ if (userUnPinned
+ && headsUpEntry.mEntry != null
+ && headsUpEntry.mEntry.mustStayOnScreen()) {
+ headsUpEntry.mEntry.setHeadsUpIsVisible();
}
};
mAvalancheController.delete(headsUpEntry, runnable, "unpinAll");
@@ -1000,7 +1025,7 @@ public class HeadsUpManagerImpl
} else {
headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)");
}
- onEntryUpdated(headsUpEntry);
+ updateTopHeadsUpFlow();
}
}
@@ -1076,7 +1101,7 @@ public class HeadsUpManagerImpl
*
* @param entry the entry that might be indirectly removed by the user's action
*
- * @see HeadsUpCoordinator#mActionPressListener
+ * @see HeadsUpCoordinator.mActionPressListener
* @see #canRemoveImmediately(String)
*/
public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
@@ -1115,8 +1140,8 @@ public class HeadsUpManagerImpl
}
/**
- * @param key
- * @return true if the entry is (pinned and expanded) or (has an active remote input)
+ * @return true if the entry with the given key is (pinned and expanded) or (has an active
+ * remote input)
*/
@Override
public boolean isSticky(String key) {
@@ -1128,7 +1153,8 @@ public class HeadsUpManagerImpl
}
@NonNull
- protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
+ @VisibleForTesting
+ HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
if (NotificationThrottleHun.isEnabled()) {
return new HeadsUpEntry(entry);
} else {
@@ -1152,7 +1178,7 @@ public class HeadsUpManagerImpl
}
@VisibleForTesting
- public final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
+ final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
if (NotificationThrottleHun.isEnabled()) {
mAvalancheController.setEnableAtRuntime(true);
if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) {
@@ -1229,14 +1255,15 @@ public class HeadsUpManagerImpl
public boolean mRemoteInputActive;
public boolean mUserActionMayIndirectlyRemove;
- protected boolean mExpanded;
- protected boolean mWasUnpinned;
+ private boolean mExpanded;
+ @VisibleForTesting
+ boolean mWasUnpinned;
@Nullable public NotificationEntry mEntry;
public long mPostTime;
public long mEarliestRemovalTime;
- @Nullable protected Runnable mRemoveRunnable;
+ @Nullable private Runnable mRemoveRunnable;
@Nullable private Runnable mCancelRemoveRunnable;
@@ -1288,7 +1315,7 @@ public class HeadsUpManagerImpl
setEntry(entry, createRemoveRunnable(entry));
}
- protected void setEntry(
+ private void setEntry(
@NonNull final NotificationEntry entry,
@Nullable Runnable removeRunnable) {
mEntry = entry;
@@ -1305,11 +1332,8 @@ public class HeadsUpManagerImpl
}
}
- protected boolean isRowPinned() {
- return mEntry != null && mEntry.isRowPinned();
- }
-
- protected void setRowPinnedStatus(PinnedStatus pinnedStatus) {
+ @VisibleForTesting
+ void setRowPinnedStatus(PinnedStatus pinnedStatus) {
if (mEntry != null) mEntry.setRowPinnedStatus(pinnedStatus);
mPinnedStatus.setValue(pinnedStatus);
}
@@ -1317,7 +1341,7 @@ public class HeadsUpManagerImpl
/**
* An interface that returns the amount of time left this HUN should show.
*/
- interface FinishTimeUpdater {
+ private interface FinishTimeUpdater {
long updateAndGetTimeRemaining();
}
@@ -1337,6 +1361,10 @@ public class HeadsUpManagerImpl
public void updateEntry(boolean updatePostTime, boolean updateEarliestRemovalTime,
@Nullable String reason) {
Runnable runnable = () -> {
+ if (mEntry == null) {
+ Log.wtf(TAG, "#updateEntry called with null mEntry; returning early");
+ return;
+ }
mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
final long now = mSystemClock.elapsedRealtime();
@@ -1358,23 +1386,18 @@ public class HeadsUpManagerImpl
FinishTimeUpdater finishTimeCalculator = () -> {
final long finishTime = calculateFinishTime();
final long now = mSystemClock.elapsedRealtime();
- final long timeLeft = NotificationThrottleHun.isEnabled()
+ return NotificationThrottleHun.isEnabled()
? Math.max(finishTime, mEarliestRemovalTime) - now
: Math.max(finishTime - now, mMinimumDisplayTime);
- return timeLeft;
};
scheduleAutoRemovalCallback(finishTimeCalculator, "updateEntry (not sticky)");
// Notify the manager, that the posted time has changed.
- onEntryUpdated(this);
+ updateTopHeadsUpFlow();
- if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
- mEntriesToRemoveAfterExpand.remove(mEntry);
- }
+ mEntriesToRemoveAfterExpand.remove(mEntry);
if (!NotificationThrottleHun.isEnabled()) {
- if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
- }
+ mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
}
}
@@ -1495,8 +1518,7 @@ public class HeadsUpManagerImpl
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
- if (o == null || !(o instanceof HeadsUpEntry)) return false;
- HeadsUpEntry otherHeadsUpEntry = (HeadsUpEntry) o;
+ if (!(o instanceof HeadsUpEntry otherHeadsUpEntry)) return false;
if (mEntry != null && otherHeadsUpEntry.mEntry != null) {
return mEntry.getKey().equals(otherHeadsUpEntry.mEntry.getKey());
}
@@ -1560,10 +1582,13 @@ public class HeadsUpManagerImpl
}
}
- public void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
+ private void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
@NonNull String reason) {
-
- mLogger.logAutoRemoveRequest(this.mEntry, reason);
+ if (mEntry == null) {
+ Log.wtf(TAG, "#scheduleAutoRemovalCallback with null mEntry; returning early");
+ return;
+ }
+ mLogger.logAutoRemoveRequest(mEntry, reason);
Runnable runnable = () -> {
long delayMs = finishTimeCalculator.updateAndGetTimeRemaining();
@@ -1603,16 +1628,14 @@ public class HeadsUpManagerImpl
public void removeAsSoonAsPossible() {
if (mRemoveRunnable != null) {
- FinishTimeUpdater finishTimeCalculator = () -> {
- final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
- return timeLeft;
- };
+ FinishTimeUpdater finishTimeCalculator = () ->
+ mEarliestRemovalTime - mSystemClock.elapsedRealtime();
scheduleAutoRemovalCallback(finishTimeCalculator, "removeAsSoonAsPossible");
}
}
/** Creates a runnable to remove this notification from the alerting entries. */
- protected Runnable createRemoveRunnable(NotificationEntry entry) {
+ private Runnable createRemoveRunnable(NotificationEntry entry) {
return () -> {
if (!NotificationThrottleHun.isEnabled()
&& !mVisualStabilityProvider.isReorderingAllowed()
@@ -1636,7 +1659,7 @@ public class HeadsUpManagerImpl
* Calculate what the post time of a notification is at some current time.
* @return the post time
*/
- protected long calculatePostTime() {
+ private long calculatePostTime() {
// The actual post time will be just after the heads-up really slided in
return mSystemClock.elapsedRealtime() + mTouchAcceptanceDelay;
}
@@ -1645,7 +1668,7 @@ public class HeadsUpManagerImpl
* @return When the notification should auto-dismiss itself, based on
* {@link SystemClock#elapsedRealtime()}
*/
- protected long calculateFinishTime() {
+ private long calculateFinishTime() {
int requestedTimeOutMs;
if (isStickyForSomeTime()) {
requestedTimeOutMs = mStickyForSomeTimeAutoDismissTime;
@@ -1659,9 +1682,8 @@ public class HeadsUpManagerImpl
/**
* Get user-preferred or default timeout duration. The larger one will be returned.
* @return milliseconds before auto-dismiss
- * @param requestedTimeout
*/
- protected int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
+ private int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
return mAccessibilityMgr.getRecommendedTimeoutMillis(
requestedTimeout,
AccessibilityManager.FLAG_CONTENT_CONTROLS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
index 80225c47e9ea..e3ca7c81582f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
@@ -44,8 +44,16 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
buffer.log(TAG, INFO, {}, { "release all immediately" })
}
- fun logShowNotificationRequest(entry: NotificationEntry) {
- buffer.log(TAG, INFO, { str1 = entry.logKey }, { "request: show notification $str1" })
+ fun logShowNotificationRequest(entry: NotificationEntry, isPinnedByUser: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ bool1 = isPinnedByUser
+ },
+ { "request: show notification $str1. isPinnedByUser=$bool1" },
+ )
}
fun logAvalancheUpdate(
@@ -86,8 +94,16 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
)
}
- fun logShowNotification(entry: NotificationEntry) {
- buffer.log(TAG, INFO, { str1 = entry.logKey }, { "show notification $str1" })
+ fun logShowNotification(entry: NotificationEntry, isPinnedByUser: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ bool1 = isPinnedByUser
+ },
+ { "show notification $str1. isPinnedByUser=$bool1" },
+ )
}
fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
@@ -140,12 +156,12 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
)
}
- fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
+ fun logAutoRemoveCanceled(entry: NotificationEntry?, reason: String?) {
buffer.log(
TAG,
INFO,
{
- str1 = entry.logKey
+ str1 = entry?.logKey
str2 = reason ?: "unknown"
},
{ "cancel auto remove of $str1 reason: $str2" },
@@ -224,29 +240,33 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
buffer.log(TAG, INFO, { str1 = entry.logKey }, { "notification removed $str1 " })
}
- fun logUpdateNotificationRequest(key: String, alert: Boolean, hasEntry: Boolean) {
+ fun logUpdateNotificationRequest(
+ key: String,
+ requestedPinnedStatus: PinnedStatus,
+ hasEntry: Boolean,
+ ) {
buffer.log(
TAG,
INFO,
{
str1 = logKey(key)
- bool1 = alert
- bool2 = hasEntry
+ bool1 = hasEntry
+ str2 = requestedPinnedStatus.name
},
- { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" },
+ { "request: update notification $str1. hasEntry: $bool1. requestedPinnedStatus: $str2" },
)
}
- fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
+ fun logUpdateNotification(key: String, requestedPinnedStatus: PinnedStatus, hasEntry: Boolean) {
buffer.log(
TAG,
INFO,
{
str1 = logKey(key)
- bool1 = alert
- bool2 = hasEntry
+ bool1 = hasEntry
+ str2 = requestedPinnedStatus.name
},
- { "update notification $str1 alert: $bool1 hasEntry: $bool2" },
+ { "update notification $str1. hasEntry: $bool2. requestedPinnedStatus: $str2" },
)
}
@@ -285,12 +305,18 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
)
}
- fun logUpdatePinnedMode(hasPinnedNotification: Boolean) {
+ fun logUpdatePinnedMode(
+ hasPinnedNotification: Boolean,
+ pinnedNotificationStatus: PinnedStatus,
+ ) {
buffer.log(
TAG,
INFO,
- { bool1 = hasPinnedNotification },
- { "has pinned notification changed to $bool1" },
+ {
+ bool1 = hasPinnedNotification
+ str1 = pinnedNotificationStatus.name
+ },
+ { "has pinned notification changed to $bool1, status=$str1" },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
index 0f19d7288f6f..9dc3ed29570b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -18,13 +18,14 @@ package com.android.systemui.statusbar.notification.icon
import android.app.Notification
import android.content.Context
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.contentDescForNotification
import javax.inject.Inject
/** Testable wrapper around Context. */
-class IconBuilder @Inject constructor(private val context: Context) {
+class IconBuilder @Inject constructor(@Main private val context: Context) {
@JvmOverloads
fun createIconView(
entry: NotificationEntry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 98ce163b81ca..b56a838a80a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -162,7 +162,9 @@ constructor(
val sbIcon = iconBuilder.createIconView(entry)
sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
val sbChipIcon: StatusBarIconView?
- if (Flags.statusBarCallChipNotificationIcon()) {
+ if (
+ Flags.statusBarCallChipNotificationIcon() && !StatusBarConnectedDisplays.isEnabled
+ ) {
sbChipIcon = iconBuilder.createIconView(entry)
sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
index fc432ba973ab..839028e4c3b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -16,62 +16,9 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
-import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
-import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
-
-/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
-class NotificationIconContainerAlwaysOnDisplayViewBinder
-@Inject
-constructor(
- private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
- private val keyguardRootViewModel: KeyguardRootViewModel,
- @ShadeDisplayAware private val configuration: ConfigurationState,
- private val failureTracker: StatusBarIconViewBindingFailureTracker,
- private val screenOffAnimationController: ScreenOffAnimationController,
- private val systemBarUtilsState: SystemBarUtilsState,
- private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
-) {
- fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
- return traceSection("NICAlwaysOnDisplay#bindWhileAttached") {
- view.repeatWhenAttached {
- lifecycleScope.launch {
- launch {
- NotificationIconContainerViewBinder.bind(
- view = view,
- viewModel = viewModel,
- configuration = configuration,
- systemBarUtilsState = systemBarUtilsState,
- failureTracker = failureTracker,
- viewStore = viewStore,
- )
- }
- launch {
- KeyguardRootViewBinder.bindAodNotifIconVisibility(
- view = view,
- isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
- configuration = configuration,
- screenOffAnimationController = screenOffAnimationController,
- )
- }
- }
- }
- }
- }
-}
/** [IconViewStore] for the always-on display. */
class AlwaysOnDisplayNotificationIconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 6dbb71463602..643ee249e75e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import android.graphics.Color
-import android.graphics.Rect
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -53,7 +52,6 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
/** Binds a view-model to a [NotificationIconContainer]. */
@@ -71,10 +69,7 @@ object NotificationIconContainerViewBinder {
launch {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
val iconColors: StateFlow<NotificationIconColors> =
- viewModel
- .iconColors(displayId)
- .mapNotNull { it.iconColors(view.viewBounds) }
- .stateIn(this)
+ viewModel.iconColors(displayId).stateIn(this)
viewModel.icons.bindIcons(
logTag = "statusbar",
view = view,
@@ -374,18 +369,6 @@ fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
getEntry(key)?.icons?.let(block)
}
-private val View.viewBounds: Rect
- get() {
- val tmpArray = intArrayOf(0, 0)
- getLocationOnScreen(tmpArray)
- return Rect(
- /* left = */ tmpArray[0],
- /* top = */ tmpArray[1],
- /* right = */ left + width,
- /* bottom = */ top + height,
- )
- }
-
private suspend inline fun <T> Flow<T>.collectTracingEach(
tag: String,
crossinline collector: (T) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
index 83f56a092bc6..124bd2eece36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
-import android.graphics.Rect
-import android.view.View
import com.android.app.tracing.traceSection
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.res.R
@@ -25,6 +23,7 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.NO_COLOR
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
+import com.android.systemui.util.view.viewBoundsOnScreen
import kotlinx.coroutines.flow.Flow
object StatusBarIconViewBinder {
@@ -60,25 +59,13 @@ object StatusBarIconViewBinder {
val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
view.staticDrawableColor =
- if (isColorized) colors.staticDrawableColor(view.viewBounds) else NO_COLOR
+ if (isColorized) colors.staticDrawableColor(view.viewBoundsOnScreen()) else NO_COLOR
// Set the color for the overflow dot
view.setDecorColor(colors.tint)
}
}
}
-private val View.viewBounds: Rect
- get() {
- val tmpArray = intArrayOf(0, 0)
- getLocationOnScreen(tmpArray)
- return Rect(
- /* left = */ tmpArray[0],
- /* top = */ tmpArray[1],
- /* right = */ left + width,
- /* bottom = */ top + height,
- )
- }
-
private suspend inline fun <T> Flow<T>.collectTracingEach(
tag: String,
crossinline collector: (T) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
index 2365db451836..a9635dcd2bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
@@ -17,14 +17,6 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel
import android.graphics.Rect
-/**
- * Lookup the colors to use for the notification icons based on the bounds of the icon container. A
- * result of `null` indicates that no color changes should be applied.
- */
-fun interface NotificationIconColorLookup {
- fun iconColors(viewBounds: Rect): NotificationIconColors?
-}
-
/** Colors to apply to notification icons. */
interface NotificationIconColors {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index f0b03065e637..2ba28a660116 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -68,18 +68,10 @@ constructor(
.distinctUntilChanged()
/** The colors with which to display the notification icons. */
- fun iconColors(displayId: Int): Flow<NotificationIconColorLookup> =
+ fun iconColors(displayId: Int): Flow<NotificationIconColors> =
darkIconInteractor
.darkState(displayId)
- .map { (areas: Collection<Rect>, tint: Int) ->
- NotificationIconColorLookup { viewBounds: Rect ->
- if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
- IconColorsImpl(tint, areas)
- } else {
- null
- }
- }
- }
+ .map { (areas: Collection<Rect>, tint: Int) -> IconColorsImpl(tint, areas) }
.flowOn(bgContext)
.conflate()
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index 9a7610ddd354..32ec02319241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -28,7 +28,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
@@ -73,7 +72,10 @@ public class HeadsUpViewBinder {
* Bind heads up view to the notification row.
* @param callback callback after heads up view is bound
*/
- public void bindHeadsUpView(NotificationEntry entry, @Nullable BindCallback callback) {
+ public void bindHeadsUpView(
+ NotificationEntry entry,
+ boolean isPinnedByUser,
+ @Nullable HeadsUpBindCallback callback) {
RowContentBindParams params = mStage.getStageParams(entry);
final boolean isImportantMessage = mNotificationMessagingUtil.isImportantMessaging(
entry.getSbn(), entry.getImportance());
@@ -84,16 +86,16 @@ public class HeadsUpViewBinder {
CancellationSignal signal = mStage.requestRebind(entry, en -> {
mLogger.entryBoundSuccessfully(entry);
en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
- // requestRebing promises that if we called cancel before this callback would be
+ // requestRebind promises that if we called cancel before this callback would be
// invoked, then we will not enter this callback, and because we always cancel before
// adding to this map, we know this will remove the correct signal.
mOngoingBindCallbacks.remove(entry);
if (callback != null) {
- callback.onBindFinished(en);
+ callback.onHeadsUpBindFinished(en, isPinnedByUser);
}
});
abortBindCallback(entry);
- mLogger.startBindingHun(entry);
+ mLogger.startBindingHun(entry, isPinnedByUser);
mOngoingBindCallbacks.put(entry, signal);
}
@@ -129,4 +131,14 @@ public class HeadsUpViewBinder {
mLogger.entryContentViewMarkedFreeable(entry);
mStage.requestRebind(entry, e -> mLogger.entryUnbound(e));
}
+
+ /**
+ * Interface for bind callback.
+ */
+ public interface HeadsUpBindCallback {
+ /**
+ * Called when all views are fully bound on the notification.
+ */
+ void onHeadsUpBindFinished(NotificationEntry entry, boolean isPinnedByUser);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index c6d2861a8c68..e690fa5a36e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -1,59 +1,63 @@
package com.android.systemui.statusbar.notification.interruption
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class HeadsUpViewBinderLogger @Inject constructor(@NotificationHeadsUpLog val buffer: LogBuffer) {
- fun startBindingHun(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "start binding heads up entry $str1 "
- })
+ fun startBindingHun(entry: NotificationEntry, isPinnedByUser: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ bool1 = isPinnedByUser
+ },
+ { "start binding heads up entry $str1. isPinnedByUser=$bool1 " },
+ )
}
fun currentOngoingBindingAborted(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "aborted potential ongoing heads up entry binding $str1 "
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = entry.logKey },
+ { "aborted potential ongoing heads up entry binding $str1 " },
+ )
}
fun entryBoundSuccessfully(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "heads up entry bound successfully $str1 "
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = entry.logKey },
+ { "heads up entry bound successfully $str1 " },
+ )
}
fun entryUnbound(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "heads up entry unbound successfully $str1 "
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = entry.logKey },
+ { "heads up entry unbound successfully $str1 " },
+ )
}
fun entryContentViewMarkedFreeable(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "start unbinding heads up entry $str1 "
- })
+ buffer.log(TAG, INFO, { str1 = entry.logKey }, { "start unbinding heads up entry $str1 " })
}
fun entryBindStageParamsNullOnUnbind(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "heads up entry bind stage params null on unbind $str1 "
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = entry.logKey },
+ { "heads up entry bind stage params null on unbind $str1 " },
+ )
}
}
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 38eaf27ad358..863c665eb4f5 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
@@ -71,12 +71,20 @@ constructor(
contentBuilder.appName = notification.loadHeaderAppName(context)
contentBuilder.subText = notification.subText()
contentBuilder.time = notification.extractWhen()
+ contentBuilder.shortCriticalText = notification.shortCriticalText()
contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
contentBuilder.title = notification.title()
contentBuilder.text = notification.text()
contentBuilder.skeletonLargeIcon = null // TODO
+ val colorsFromNotif = recoveredBuilder.getColors(/* header= */ false)
+ contentBuilder.colors =
+ PromotedNotificationContentModel.Colors(
+ backgroundColor = colorsFromNotif.backgroundColor,
+ primaryTextColor = colorsFromNotif.primaryTextColor,
+ )
+
recoveredBuilder.style?.extractContent(contentBuilder)
?: run { contentBuilder.style = Style.Ineligible }
@@ -90,6 +98,13 @@ private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_T
private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT)
+private fun Notification.shortCriticalText(): String? {
+ if (!android.app.Flags.apiRichOngoing()) {
+ return null
+ }
+ return this.shortCriticalText
+}
+
private fun Notification.chronometerCountDown(): Boolean =
extras?.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, /* defaultValue= */ false) ?: false
@@ -100,7 +115,7 @@ private fun Notification.extractWhen(): When? {
val countDown = chronometerCountDown()
return when {
- showsTime -> When(time, When.Mode.Absolute)
+ showsTime -> When(time, When.Mode.BasicTime)
showsChronometer -> When(time, if (countDown) When.Mode.CountDown else When.Mode.CountUp)
else -> null
}
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 41ee3b992c5a..fe2dabe1ba8a 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
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.promoted.shared.model
import android.annotation.DrawableRes
import android.graphics.drawable.Icon
+import androidx.annotation.ColorInt
import com.android.internal.widget.NotificationProgressModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
@@ -33,12 +34,18 @@ data class PromotedNotificationContentModel(
val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
val appName: CharSequence?,
val subText: CharSequence?,
+ val shortCriticalText: String?,
+ /**
+ * The timestamp associated with the notification. Null if the timestamp should not be
+ * displayed.
+ */
val time: When?,
val lastAudiblyAlertedMs: Long,
@DrawableRes val profileBadgeResId: Int?,
val title: CharSequence?,
val text: CharSequence?,
val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val colors: Colors,
val style: Style,
// for CallStyle:
@@ -55,12 +62,14 @@ data class PromotedNotificationContentModel(
var appName: CharSequence? = null
var subText: CharSequence? = null
var time: When? = null
+ var shortCriticalText: String? = null
var lastAudiblyAlertedMs: Long = 0L
@DrawableRes var profileBadgeResId: Int? = null
var title: CharSequence? = null
var text: CharSequence? = null
var skeletonLargeIcon: Icon? = null
var style: Style = Style.Ineligible
+ var colors: Colors = Colors(backgroundColor = 0, primaryTextColor = 0)
// for CallStyle:
var personIcon: Icon? = null
@@ -77,12 +86,14 @@ data class PromotedNotificationContentModel(
skeletonSmallIcon = skeletonSmallIcon,
appName = appName,
subText = subText,
+ shortCriticalText = shortCriticalText,
time = time,
lastAudiblyAlertedMs = lastAudiblyAlertedMs,
profileBadgeResId = profileBadgeResId,
title = title,
text = text,
skeletonLargeIcon = skeletonLargeIcon,
+ colors = colors,
style = style,
personIcon = personIcon,
personName = personName,
@@ -96,12 +107,18 @@ data class PromotedNotificationContentModel(
data class When(val time: Long, val mode: Mode) {
/** The mode used to display a notification's `when` value. */
enum class Mode {
- Absolute,
+ /** No custom mode requested by the notification. */
+ BasicTime,
+ /** Show the notification's time as a chronometer that counts down to [time]. */
CountDown,
+ /** Show the notification's time as a chronometer that counts up from [time]. */
CountUp,
}
}
+ /** The colors used to display the notification. */
+ data class Colors(@ColorInt val backgroundColor: Int, @ColorInt val primaryTextColor: Int)
+
/** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
enum class Style {
BigPicture,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 5c51adadfd82..b4092cd785bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -37,7 +37,6 @@ import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
-import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
@@ -123,8 +122,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void updateColors() {
- mNormalColor = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorSurfaceContainerHigh);
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index a8d59d83d1e9..6bfc9f07ffc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -40,6 +40,7 @@ import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
private const val TAG = "ChannelDialogController"
@@ -58,11 +59,10 @@ private const val TAG = "ChannelDialogController"
*/
@SysUISingleton
class ChannelEditorDialogController @Inject constructor(
- c: Context,
+ @ShadeDisplayAware private val context: Context,
private val noMan: INotificationManager,
private val dialogBuilder: ChannelEditorDialog.Builder
) {
- val context: Context = c.applicationContext
private var prepared = false
private lateinit var dialog: ChannelEditorDialog
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 c8811fd3b76d..5a52c379d0d6 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
@@ -1247,6 +1247,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public PinnedStatus getPinnedStatus() {
+ return mPinnedStatus;
+ }
+
+ @Override
public int getPinnedHeadsUpHeight() {
return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
}
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 ef6cad134534..f83a1d9b7833 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
@@ -40,6 +40,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.RoundableState;
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.util.Compile;
@@ -201,6 +202,11 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
return false;
}
+ @NonNull
+ public PinnedStatus getPinnedStatus() {
+ return PinnedStatus.NotPinned;
+ }
+
public boolean isHeadsUpAnimatingAway() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index 61f4e96bf99a..5c4c253d1f98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -21,7 +21,6 @@ import static android.app.Notification.COLOR_INVALID;
import android.annotation.Nullable;
import android.app.Flags;
import android.content.Context;
-import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
@@ -115,16 +114,9 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
}
private void resolveThemeTextColors() {
- try (TypedArray ta = mContext.getTheme().obtainStyledAttributes(
- android.R.style.Theme_DeviceDefault_DayNight, new int[]{
- com.android.internal.R.attr.materialColorOnSurface,
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- })) {
- if (ta != null) {
- mPrimaryTextColor = ta.getColor(0, mPrimaryTextColor);
- mSecondaryTextColor = ta.getColor(1, mSecondaryTextColor);
- }
- }
+ mPrimaryTextColor = mContext.getColor(com.android.internal.R.color.materialColorOnSurface);
+ mSecondaryTextColor = mContext.getColor(
+ com.android.internal.R.color.materialColorOnSurfaceVariant);
}
public void bind(@Nullable CharSequence title, @Nullable CharSequence text,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 34ef63944f14..e440d2728263 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -35,7 +35,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.util.ContrastColorUtil;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
@@ -83,8 +82,8 @@ public class NotificationBackgroundView extends View implements Dumpable,
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
- mNormalColor = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorSurfaceContainerHigh);
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
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 e141b7cf23ec..be9f60d4d5a1 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
@@ -43,7 +43,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -337,10 +336,7 @@ public class NotificationConversationInfo extends LinearLayout implements
Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo);
if (person == null) {
person = mContext.getDrawable(R.drawable.ic_person).mutate();
- TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.materialColorPrimary});
- int colorPrimary = ta.getColor(0, 0);
- ta.recycle();
+ int colorPrimary = mContext.getColor(com.android.internal.R.color.materialColorPrimary);
person.setTint(colorPrimary);
}
ImageView image = findViewById(R.id.conversation_icon);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index f8aff69f0531..9d13ab53ec71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -21,7 +21,6 @@ import android.content.Context
import android.graphics.drawable.AnimatedImageDrawable
import android.view.View
import android.view.ViewGroup
-import android.view.ViewGroup.MarginLayoutParams
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingGroup
@@ -94,13 +93,6 @@ class NotificationConversationTemplateViewWrapper(
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
resolveViews()
- if (Flags.notificationsRedesignAppIcons() && row.isShowingAppIcon) {
- // Override the margins to be 2dp instead of 4dp according to the new design if we're
- // showing the app icon.
- val lp = badgeIconView.layoutParams as MarginLayoutParams
- lp.setMargins(2, 2, 2, 2)
- badgeIconView.layoutParams = lp
- }
super.onContentUpdated(row)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 182fba34cafb..752a8abf055d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -38,7 +38,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
-import com.android.settingslib.Utils;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.FeedbackIcon;
@@ -344,9 +343,8 @@ public abstract class NotificationViewWrapper implements TransformableView {
if (customBackgroundColor != 0) {
return customBackgroundColor;
}
- return Utils.getColorAttr(mView.getContext(),
- com.android.internal.R.attr.materialColorSurfaceContainerHigh)
- .getDefaultColor();
+ return mView.getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
public void setLegacy(boolean legacy) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c8e18a8fe5ca..00cd8ce87738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -16,11 +16,12 @@
package com.android.systemui.statusbar.notification.stack;
+import static android.app.Flags.notificationsRedesignTemplates;
+
import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Path.Direction;
@@ -171,7 +172,9 @@ public class NotificationChildrenContainer extends ViewGroup
R.dimen.notification_children_container_margin_top);
mNotificationTopPadding = res.getDimensionPixelOffset(
R.dimen.notification_children_container_top_padding);
- mHeaderHeight = mNotificationHeaderMargin + mNotificationTopPadding;
+ mHeaderHeight = notificationsRedesignTemplates()
+ ? res.getDimensionPixelSize(R.dimen.notification_2025_header_height)
+ : mNotificationHeaderMargin + mNotificationTopPadding;
mCollapsedBottomPadding = res.getDimensionPixelOffset(
R.dimen.notification_children_collapsed_bottom_padding);
mEnableShadowOnChildNotifications =
@@ -1515,10 +1518,9 @@ public class NotificationChildrenContainer extends ViewGroup
int color = mContainingNotification.getNotificationColor();
Resources.Theme theme = new ContextThemeWrapper(mContext,
com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
- try (TypedArray ta = theme.obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.materialColorPrimary})) {
- color = ta.getColor(0, color);
- }
+
+ color = mContext.getColor(com.android.internal.R.color.materialColorPrimary);
+
mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
}
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 b9e38abf8ab2..7c9d850eaf07 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
@@ -83,7 +83,6 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardSliceView;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
@@ -1285,7 +1284,11 @@ public class NotificationStackScrollLayout
@Override
public void setStackCutoff(float stackCutoff) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- mAmbientState.setStackCutoff(stackCutoff);
+ if (mAmbientState.getStackCutoff() != stackCutoff) {
+ mAmbientState.setStackCutoff(stackCutoff);
+ updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction());
+ requestChildrenUpdate();
+ }
}
@Override
@@ -2630,6 +2633,7 @@ public class NotificationStackScrollLayout
private void updateContentHeight() {
if (SceneContainerFlag.isEnabled()) {
updateIntrinsicStackHeight();
+ updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction());
return;
}
@@ -4729,10 +4733,10 @@ public class NotificationStackScrollLayout
* Update colors of section headers, shade footer, and empty shade views.
*/
void updateDecorViews() {
- final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.materialColorOnSurface);
- final @ColorInt int onSurfaceVariant = Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.materialColorOnSurfaceVariant);
+ final @ColorInt int onSurface = mContext.getColor(
+ com.android.internal.R.color.materialColorOnSurface);
+ final @ColorInt int onSurfaceVariant = mContext.getColor(
+ com.android.internal.R.color.materialColorOnSurfaceVariant);
ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
if (colorUpdateLogger != null) {
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 ba707a5cd0b1..245b1d29fb79 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
@@ -429,9 +429,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
};
/**
- * Recalculate sensitiveness without animation; called when waking up while keyguard occluded.
+ * Recalculate sensitiveness without animation; called when waking up while keyguard occluded,
+ * or whenever we update the Lockscreen public mode.
*/
- public void updateSensitivenessForOccludedWakeup() {
+ public void updateSensitivenessWithoutAnimation() {
updateSensitivenessWithAnimation(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 969ff1b4ffe7..44075afa6b40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK;
+
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -28,6 +30,8 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.core.view.ViewCompat;
+
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -69,6 +73,13 @@ public class SectionHeaderView extends StackScrollerDecorView {
mLabelView.setText(mLabelTextId);
}
mLabelView.setAccessibilityHeading(true);
+ ViewCompat.replaceAccessibilityAction(
+ mLabelView,
+ ACTION_CLICK,
+ getResources().getString(
+ R.string.accessibility_notification_section_header_open_settings),
+ null
+ );
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index bffcae99e7f6..b4561686b7b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -147,8 +147,7 @@ constructor(
// The footer needs to be re-inflated every time the theme or the font size changes.
configuration
.inflateLayout<FooterView>(
- if (NotifRedesignFooter.isEnabled)
- R.layout.status_bar_notification_footer_redesign
+ if (NotifRedesignFooter.isEnabled) R.layout.notification_2025_footer
else R.layout.status_bar_notification_footer,
parentView,
attachToRoot = false,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index c5bef99f9307..ef68b4de5291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -109,11 +109,6 @@ constructor(
}
}
launch {
- viewModel.shouldResetStackTop
- .filter { it }
- .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
- }
- launch {
viewModel.shouldCloseGuts
.filter { it }
.collectTraced { view.closeGutsOnSceneTouch() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 56b335648138..1bb205cbcb61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -192,12 +192,6 @@ constructor(
/** Whether we should close any open notification guts. */
val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts
- val shouldResetStackTop: Flow<Boolean> =
- sceneInteractor.transitionState
- .mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
- .distinctUntilChanged()
- .dumpWhileCollecting("shouldResetStackTop")
-
/** Whether the Notification Stack is visibly on the lockscreen scene. */
val isShowingStackOnLockscreen: Flow<Boolean> =
sceneInteractor.transitionState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index d1338eadb6b5..f2ef2f0ab48f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -313,6 +313,7 @@ constructor(
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true)
}
+ intent.collectExtraIntentKeys()
try {
result[0] =
ActivityTaskManager.getService()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 7f95fb072ede..adfcb710da92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -53,6 +53,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -209,10 +210,16 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
@Override
public void clickTile(ComponentName tile) {
- // Can't inject this because it changes with the QS fragment
- QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
- if (qsPanelController != null) {
- qsPanelController.clickTile(tile);
+ if (QsInCompose.isEnabled()) {
+ if (tile != null) {
+ mQSHost.clickTile(tile);
+ }
+ } else {
+ // Can't inject this because it changes with the QS fragment
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
+ if (qsPanelController != null) {
+ qsPanelController.clickTile(tile);
+ }
}
}
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 c6af3280eef1..7bea4800f7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2234,6 +2234,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// If the state didn't change, we may still need to update public mode
mLockscreenUserManager.updatePublicMode();
+ if (SceneContainerFlag.isEnabled()) {
+ mStackScrollerController.updateSensitivenessWithoutAnimation();
+ }
}
if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mStatusBarStateController.isKeyguardRequested()) {
@@ -2681,7 +2684,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// So if AOD is off or unsupported we need to trigger these updates at screen on
// when the keyguard is occluded.
mLockscreenUserManager.updatePublicMode();
- mStackScrollerController.updateSensitivenessForOccludedWakeup();
+ mStackScrollerController.updateSensitivenessWithoutAnimation();
}
if (mLaunchCameraWhenFinishedWaking) {
startLaunchTransitionTimeout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
index fe5a02be2fb3..153dd990820d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -67,6 +67,7 @@ class ComponentSystemUIDialog(
broadcastDispatcher,
dialogTransitionAnimator,
delegate,
+ true, /* shouldAcsdDismissDialog */
),
LifecycleOwner,
SavedStateRegistryOwner,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 858cac111525..9c7af181284e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -65,6 +65,13 @@ constructor(@Assisted private val context: Context) :
listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
}
+ override fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration) {
+ val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onMovedToDisplay(newDisplayId, newConfiguration)
+ }
+ }
+
override fun onConfigurationChanged(newConfig: Configuration) {
// Avoid concurrent modification exception
val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
index 8f4279e80376..a324e6df8a33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.CoreStartable
-import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import javax.inject.Inject
@@ -27,8 +27,8 @@ import javax.inject.Inject
class ConfigurationControllerStartable
@Inject
constructor(
- @GlobalConfig private val configurationController: ConfigurationController,
- private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
+ @Main private val configurationController: ConfigurationController,
+ private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>,
) : CoreStartable {
override fun start() {
listeners.forEach { configurationController.addCallback(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
index 3fd46fc484a9..537e3e1893b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
@@ -28,4 +28,13 @@ import android.content.res.Configuration
interface ConfigurationForwarder {
/** Should be called when a new configuration is received. */
fun onConfigurationChanged(newConfiguration: Configuration)
+
+ /**
+ * Should be called when the view associated to this configuration forwarded moved to another
+ * display, usually as a consequence of [View.onMovedToDisplay].
+ *
+ * For the default configuration forwarder (associated with the global configuration) this is
+ * never expected to be called.
+ */
+ fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 6cad68ffe8fb..53a29505510b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -23,6 +23,7 @@ import android.util.MathUtils;
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.SourceType;
@@ -153,7 +155,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (shouldBeVisible()) {
+ if (shouldHeadsUpStatusBarBeVisible()) {
updateTopEntry();
// trigger scroller to notify the latest panel translation
@@ -217,35 +219,54 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
private void updateTopEntry() {
NotificationEntry newEntry = null;
- if (shouldBeVisible()) {
+ if (shouldHeadsUpStatusBarBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
NotificationEntry previousEntry = mView.getShowingEntry();
mView.setEntry(newEntry);
if (newEntry != previousEntry) {
if (newEntry == null) {
- // no heads up anymore, lets start the disappear animation
+ // No longer heads up
setPinnedStatus(PinnedStatus.NotPinned);
} else if (previousEntry == null) {
- // We now have a headsUp and didn't have one before. Let's start the disappear
- // animation
- setPinnedStatus(PinnedStatus.PinnedBySystem);
+ // We now have a heads up when we didn't have one before
+ setPinnedStatus(newEntry.getPinnedStatus());
}
- String isolatedIconKey;
- if (newEntry != null) {
- isolatedIconKey = newEntry.getRepresentativeEntry().getKey();
+ mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
+ getIsolatedIconKey(newEntry));
+ }
+ }
+
+ private static @Nullable String getIsolatedIconKey(NotificationEntry newEntry) {
+ if (newEntry == null) {
+ return null;
+ }
+ if (StatusBarNotifChips.isEnabled()) {
+ // If the flag is on, only show the isolated icon if the HUN is pinned by the
+ // *system*. (If the HUN was pinned by the user, then the user tapped the
+ // notification status bar chip and we want to keep the chip showing.)
+ if (newEntry.getPinnedStatus() == PinnedStatus.PinnedBySystem) {
+ return newEntry.getRepresentativeEntry().getKey();
} else {
- isolatedIconKey = null;
+ return null;
}
- mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(isolatedIconKey);
+ } else {
+ // If the flag is off, we know all HUNs are pinned by the system and should show
+ // the isolated icon
+ return newEntry.getRepresentativeEntry().getKey();
}
}
private void setPinnedStatus(PinnedStatus pinnedStatus) {
if (mPinnedStatus != pinnedStatus) {
mPinnedStatus = pinnedStatus;
- if (pinnedStatus.isPinned()) {
+
+ boolean shouldShowHunStatusBar = StatusBarNotifChips.isEnabled()
+ ? mPinnedStatus == PinnedStatus.PinnedBySystem
+ // If the flag isn't enabled, all HUNs get the normal treatment.
+ : mPinnedStatus.isPinned();
+ if (shouldShowHunStatusBar) {
updateParentClipping(false /* shouldClip */);
mView.setVisibility(View.VISIBLE);
show(mView);
@@ -333,23 +354,36 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
return mPinnedStatus;
}
- /**
- * Should the headsup status bar view be visible right now? This may be different from isShown,
- * since the headsUp manager might not have notified us yet of the state change.
- *
- * @return if the heads up status bar view should be shown
- * @deprecated use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
- */
- public boolean shouldBeVisible() {
+ /** True if the device's current state allows us to show HUNs and false otherwise. */
+ private boolean canShowHeadsUp() {
boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
- boolean canShow = !isExpanded() && notificationsShown;
if (mBypassController.getBypassEnabled() &&
(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
|| mKeyguardStateController.isKeyguardGoingAway())
&& notificationsShown) {
- canShow = true;
+ return true;
+ }
+ return !isExpanded() && notificationsShown;
+ }
+
+ /**
+ * True if the headsup status bar view (which has just the HUN icon and app name) should be
+ * visible right now and false otherwise.
+ *
+ * @deprecated use {@link com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor#getStatusBarHeadsUpState()}
+ * instead.
+ */
+ @Deprecated
+ public boolean shouldHeadsUpStatusBarBeVisible() {
+ if (StatusBarNotifChips.isEnabled()) {
+ return canShowHeadsUp()
+ && mHeadsUpManager.pinnedHeadsUpStatus() == PinnedStatus.PinnedBySystem;
+ // Note: This means that if mHeadsUpManager.pinnedHeadsUpStatus() == PinnedByUser,
+ // #updateTopEntry won't do anything, so mPinnedStatus will remain as NotPinned and will
+ // *not* update to PinnedByUser.
+ } else {
+ return canShowHeadsUp() && mHeadsUpManager.hasPinnedHeadsUp();
}
- return canShow && mHeadsUpManager.hasPinnedHeadsUp();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 1cca3ae0a2c0..d7cc65d22663 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -180,6 +180,7 @@ constructor(
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true)
}
+ intent.collectExtraIntentKeys()
try {
result[0] =
ActivityTaskManager.getService()
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 f19d707046f1..2467e0890100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -39,10 +39,12 @@ import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -83,6 +85,7 @@ private constructor(
private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
) : ViewController<PhoneStatusBarView>(view) {
private lateinit var battery: BatteryMeterView
@@ -296,7 +299,19 @@ private constructor(
return true
}
}
- return shadeViewController.handleExternalTouch(event)
+
+ // With the StatusBarConnectedDisplays changes, status bar touches should result in
+ // shade interaction only if ShadeWindowGoesAround.isEnabled or if touch is on the
+ // display which currently hosts the shade.
+ return if (
+ !StatusBarConnectedDisplays.isEnabled ||
+ ShadeWindowGoesAround.isEnabled ||
+ context.displayId == shadeDisplaysRepository.displayId.value
+ ) {
+ shadeViewController.handleExternalTouch(event)
+ } else {
+ false
+ }
}
}
@@ -352,6 +367,7 @@ private constructor(
@DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
@@ -380,6 +396,7 @@ private constructor(
darkIconDispatcher,
statusBarContentInsetsProviderStore.defaultDisplay,
lazyStatusBarShadeDisplayPolicy,
+ shadeDisplaysRepository,
)
}
}
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 e7c6fb46f984..324db79a4078 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -51,7 +51,6 @@ import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -82,9 +81,6 @@ import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -93,6 +89,9 @@ import java.util.function.Consumer;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
@@ -1532,17 +1531,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void updateThemeColors() {
if (mScrimBehind == null) return;
- int background = Utils.getColorAttr(mScrimBehind.getContext(),
- com.android.internal.R.attr.materialColorSurfaceDim).getDefaultColor();
- int accent = Utils.getColorAttr(mScrimBehind.getContext(),
- com.android.internal.R.attr.materialColorPrimary).getDefaultColor();
+ int background = mScrimBehind.getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceDim);
+ int accent = mScrimBehind.getContext().getColor(
+ com.android.internal.R.color.materialColorPrimary);
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
mColors.setSupportsDarkText(isBackgroundLight);
- int surface = Utils.getColorAttr(mScrimBehind.getContext(),
- com.android.internal.R.attr.materialColorSurface).getDefaultColor();
+ int surface = mScrimBehind.getContext().getColor(
+ com.android.internal.R.color.materialColorSurface);
for (ScrimState state : ScrimState.values()) {
state.setSurfaceColor(surface);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index b3cc047251ed..0fac6448909c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,10 +37,12 @@ import androidx.annotation.NonNull;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
@@ -59,6 +61,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
@@ -69,7 +72,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.Set;
@@ -102,6 +104,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
private final NotificationListContainer mNotifListContainer;
+ private final DeviceUnlockedInteractor mDeviceUnlockedInteractor;
private final QuickSettingsController mQsController;
protected boolean mVrMode;
@@ -133,7 +136,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
NotificationRemoteInputManager remoteInputManager,
NotificationRemoteInputManager.Callback remoteInputManagerCallback,
- NotificationListContainer notificationListContainer) {
+ NotificationListContainer notificationListContainer,
+ DeviceUnlockedInteractor deviceUnlockedInteractor) {
mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -160,6 +164,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mNotifListContainer = notificationListContainer;
+ mDeviceUnlockedInteractor = deviceUnlockedInteractor;
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
@@ -248,14 +253,25 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
} else if (clickedEntry.isSensitive().getValue()
- && mDynamicPrivacyController.isInLockedDownShade()) {
+ && isInLockedDownShade()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ // launch the bouncer
mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */
, null /* cancelRunnable */, false /* afterKeyguardGone */);
}
}
}
+ /** @return true if the Shade is shown over the Lockscreen, and the device is locked */
+ private boolean isInLockedDownShade() {
+ if (SceneContainerFlag.isEnabled()) {
+ return mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ && !mDeviceUnlockedInteractor.getDeviceUnlockStatus().getValue().isUnlocked();
+ } else {
+ return mDynamicPrivacyController.isInLockedDownShade();
+ }
+ }
+
@Override
public boolean isDeviceInVrMode() {
return mVrMode;
@@ -300,7 +316,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
|| mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
- boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+ boolean needsRedaction = mLockscreenUserManager.getRedactionType(entry)
+ != NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
if (userPublic && needsRedaction) {
// TODO(b/135046837): we can probably relax this with dynamic privacy
return true;
@@ -353,7 +370,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
return false;
}
- if (!mLockscreenUserManager.needsRedaction(entry)) {
+ if (mLockscreenUserManager.getRedactionType(entry)
+ == NotificationLockscreenUserManager.REDACTION_TYPE_NONE) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 0ad1042a665f..03324d2a3e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -145,7 +145,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
*/
public SystemUIDialog create() {
return create(new DialogDelegate<>() {
- }, mContext, DEFAULT_THEME);
+ }, mContext, DEFAULT_THEME, true /* shouldAcsdDismissDialog */);
}
/**
@@ -155,7 +155,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
*/
public SystemUIDialog create(Context context) {
return create(new DialogDelegate<>() {
- }, context, DEFAULT_THEME);
+ }, context, DEFAULT_THEME, true /* shouldAcsdDismissDialog */);
}
/**
@@ -168,8 +168,21 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
return create(delegate, context, DEFAULT_THEME);
}
+ /**
+ * Creates a new instance of {@link SystemUIDialog} with {@code delegate} as the {@link
+ * Delegate}. When you need to customize the dialog, pass it a delegate.
+ *
+ * This method allows the caller to specify if the dialog should be dismissed in response
+ * to the ACTION_CLOSE_SYSTEM_DIALOGS intent.
+ */
+ public SystemUIDialog create(Delegate delegate, Context context,
+ boolean shouldAcsdDismissDialog) {
+ return create(delegate, context, DEFAULT_THEME, shouldAcsdDismissDialog);
+ }
+
public SystemUIDialog create(Delegate delegate, Context context, @StyleRes int theme) {
- return create((DialogDelegate<SystemUIDialog>) delegate, context, theme);
+ return create((DialogDelegate<SystemUIDialog>) delegate, context, theme,
+ true /* shouldAcsdDismissDialog */);
}
public SystemUIDialog create(Delegate delegate) {
@@ -177,7 +190,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
}
private SystemUIDialog create(DialogDelegate<SystemUIDialog> dialogDelegate,
- Context context, @StyleRes int theme) {
+ Context context, @StyleRes int theme, boolean shouldAcsdDismissDialog) {
return new SystemUIDialog(
context,
theme,
@@ -186,7 +199,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
mSysUiState,
mBroadcastDispatcher,
mDialogTransitionAnimator,
- dialogDelegate);
+ dialogDelegate,
+ shouldAcsdDismissDialog);
}
}
@@ -207,7 +221,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
broadcastDispatcher,
dialogTransitionAnimator,
new DialogDelegate<>() {
- });
+ },
+ true /* shouldAcsdDismissDialog */);
}
public SystemUIDialog(
@@ -227,7 +242,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
sysUiState,
broadcastDispatcher,
dialogTransitionAnimator,
- (DialogDelegate<SystemUIDialog>) delegate);
+ (DialogDelegate<SystemUIDialog>) delegate,
+ true /* shouldAcsdDismissDialog */);
}
public SystemUIDialog(
@@ -238,7 +254,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
SysUiState sysUiState,
BroadcastDispatcher broadcastDispatcher,
DialogTransitionAnimator dialogTransitionAnimator,
- DialogDelegate<SystemUIDialog> delegate) {
+ DialogDelegate<SystemUIDialog> delegate,
+ boolean shouldAcsdDismissDialog) {
super(context, theme);
mContext = context;
mDelegate = delegate;
@@ -249,7 +266,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
getWindow().setAttributes(attrs);
mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this, broadcastDispatcher,
- dialogTransitionAnimator) : null;
+ dialogTransitionAnimator, shouldAcsdDismissDialog) : null;
mDialogManager = dialogManager;
mSysUiState = sysUiState;
}
@@ -523,7 +540,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
// TODO(b/219008720): Remove those calls to Dependency.get.
DismissReceiver dismissReceiver = new DismissReceiver(dialog,
Dependency.get(BroadcastDispatcher.class),
- Dependency.get(DialogTransitionAnimator.class));
+ Dependency.get(DialogTransitionAnimator.class),
+ true /* shouldAcsdDismissDialog */);
dialog.setOnDismissListener(d -> {
dismissReceiver.unregister();
if (dismissAction != null) dismissAction.run();
@@ -595,12 +613,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
}
private static class DismissReceiver extends BroadcastReceiver {
- private static final IntentFilter INTENT_FILTER = new IntentFilter();
-
- static {
- INTENT_FILTER.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- INTENT_FILTER.addAction(Intent.ACTION_SCREEN_OFF);
- }
+ private final IntentFilter mIntentFilter = new IntentFilter();
private final Dialog mDialog;
private boolean mRegistered;
@@ -608,14 +621,20 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
private final DialogTransitionAnimator mDialogTransitionAnimator;
DismissReceiver(Dialog dialog, BroadcastDispatcher broadcastDispatcher,
- DialogTransitionAnimator dialogTransitionAnimator) {
+ DialogTransitionAnimator dialogTransitionAnimator,
+ boolean shouldAcsdDismissDialog) {
mDialog = dialog;
mBroadcastDispatcher = broadcastDispatcher;
mDialogTransitionAnimator = dialogTransitionAnimator;
+
+ mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ if (shouldAcsdDismissDialog) {
+ mIntentFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ }
}
void register() {
- mBroadcastDispatcher.registerReceiver(this, INTENT_FILTER, null, UserHandle.CURRENT);
+ mBroadcastDispatcher.registerReceiver(this, mIntentFilter, null, UserHandle.CURRENT);
mRegistered = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
index b9cba9903466..d0d0b5c402e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -29,7 +29,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.settingslib.Utils;
import com.android.systemui.res.R;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -51,8 +50,8 @@ public class TapAgainView extends TextView {
}
void updateColor() {
- final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorOnSurface);
+ final @ColorInt int onSurface = mContext.getColor(
+ com.android.internal.R.color.materialColorOnSurface);
setTextColor(onSurface);
setBackground(getResources().getDrawable(R.drawable.rounded_bg_full, mContext.getTheme()));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index e4768e83f7d0..d257288637df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -377,6 +377,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
mHomeStatusBarViewBinder.bind(
+ view.getContext().getDisplayId(),
mStatusBar,
mHomeStatusBarViewModel,
/* systemEventChipAnimateIn */ null,
@@ -626,8 +627,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
StatusBarRootModernization.assertInLegacyMode();
// TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
- boolean headsUpVisible =
- mHomeStatusBarComponent.getHeadsUpAppearanceController().shouldBeVisible();
+ boolean headsUpVisible = mHomeStatusBarComponent
+ .getHeadsUpAppearanceController()
+ .shouldHeadsUpStatusBarBeVisible();
if (SceneContainerFlag.isEnabled()) {
// With the scene container, only use the value calculated by the view model to
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 216630409160..c57cede754d3 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
@@ -260,6 +260,7 @@ constructor(
startTimeMs = currentInfo.callStartTime,
notificationIconView = icon,
intent = currentInfo.intent,
+ notificationKey = currentInfo.key,
)
} else {
return OngoingCallModel.NoCall
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 1f7bd1418543..2f7b24393ae0 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
@@ -16,22 +16,34 @@
package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
import com.android.systemui.activity.data.repository.ActivityManagerRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
+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.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
/**
@@ -47,56 +59,146 @@ import kotlinx.coroutines.flow.stateIn
@SysUISingleton
class OngoingCallInteractor @Inject constructor(
@Application private val scope: CoroutineScope,
- activityManagerRepository: ActivityManagerRepository,
+ private val activityManagerRepository: ActivityManagerRepository,
+ private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
activeNotificationsInteractor: ActiveNotificationsInteractor,
@OngoingCallLog private val logBuffer: LogBuffer,
-) {
+) : CoreStartable {
private val logger = Logger(logBuffer, TAG)
/**
+ * Tracks whether the call chip has been swiped away.
+ */
+ private val _isChipSwipedAway = MutableStateFlow(false)
+ val isChipSwipedAway: StateFlow<Boolean> = _isChipSwipedAway.asStateFlow()
+
+ /**
* The current state of ongoing calls.
*/
val ongoingCallState: StateFlow<OngoingCallModel> =
activeNotificationsInteractor.ongoingCallNotification
- .flatMapLatest { notificationModel ->
- when (notificationModel) {
- null -> {
- logger.d("No active call notification - hiding chip")
- flowOf(OngoingCallModel.NoCall)
- }
-
- else -> combine(
- flowOf(notificationModel),
- activityManagerRepository.createIsAppVisibleFlow(
- creationUid = notificationModel.uid,
- logger = logger,
- identifyingLogTag = TAG,
- ),
- ) { model, isVisible ->
- when {
- isVisible -> {
- logger.d({ "Call app is visible: uid=$int1" }) {
- int1 = model.uid
- }
- OngoingCallModel.InCallWithVisibleApp
- }
-
- else -> {
- logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
- long1 = model.whenTime
- bool1 = model.statusBarChipIconView != null
- }
- OngoingCallModel.InCall(
- startTimeMs = model.whenTime,
- notificationIconView = model.statusBarChipIconView,
- intent = model.contentIntent,
- )
- }
- }
- }
+ .flatMapLatest { notification ->
+ createOngoingCallStateFlow(
+ notification = notification
+ )
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = OngoingCallModel.NoCall
+ )
+
+ @VisibleForTesting
+ val isStatusBarRequiredForOngoingCall = combine(
+ ongoingCallState,
+ isChipSwipedAway
+ ) { callState, chipSwipedAway ->
+ callState is OngoingCallModel.InCall && !chipSwipedAway
+ }
+
+ @VisibleForTesting
+ val isGestureListeningEnabled = combine(
+ ongoingCallState,
+ statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode,
+ isChipSwipedAway
+ ) { callState, isFullscreen, chipSwipedAway ->
+ callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen
+ }
+
+ private fun createOngoingCallStateFlow(
+ notification: ActiveNotificationModel?
+ ): Flow<OngoingCallModel> {
+ if (notification == null) {
+ logger.d("No active call notification - hiding chip")
+ return flowOf(OngoingCallModel.NoCall)
+ }
+
+ return combine(
+ flowOf(notification),
+ activityManagerRepository.createIsAppVisibleFlow(
+ creationUid = notification.uid,
+ logger = logger,
+ identifyingLogTag = TAG,
+ )
+ ) { model, isVisible ->
+ deriveOngoingCallState(model, isVisible)
+ }
+ }
+
+ override fun start() {
+ ongoingCallState
+ .filterIsInstance<OngoingCallModel.NoCall>()
+ .onEach {
+ _isChipSwipedAway.value = false
+ }.launchIn(scope)
+
+ isStatusBarRequiredForOngoingCall.onEach { statusBarRequired ->
+ setStatusBarRequiredForOngoingCall(statusBarRequired)
+ }.launchIn(scope)
+
+ isGestureListeningEnabled.onEach { isEnabled ->
+ updateGestureListening(isEnabled)
+ }.launchIn(scope)
+ }
+
+ /**
+ * Callback that must run when the status bar is swiped while gesture listening is active.
+ */
+ @VisibleForTesting
+ fun onStatusBarSwiped() {
+ logger.d("Status bar chip swiped away")
+ _isChipSwipedAway.value = true
+ }
+
+ private fun deriveOngoingCallState(
+ model: ActiveNotificationModel,
+ isVisible: Boolean
+ ): OngoingCallModel {
+ return when {
+ isVisible -> {
+ logger.d({ "Call app is visible: uid=$int1" }) {
+ int1 = model.uid
+ }
+ OngoingCallModel.InCallWithVisibleApp
+ }
+
+ else -> {
+ logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
+ long1 = model.whenTime
+ bool1 = model.statusBarChipIconView != null
}
+ OngoingCallModel.InCall(
+ startTimeMs = model.whenTime,
+ notificationIconView = model.statusBarChipIconView,
+ intent = model.contentIntent,
+ notificationKey = model.key
+ )
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingCallModel.NoCall)
+ }
+ }
+
+ private fun setStatusBarRequiredForOngoingCall(statusBarRequired: Boolean) {
+ // TODO(b/382808183): Create a single repository that can be utilized in
+ // `statusBarModeRepositoryStore` and `statusBarWindowControllerStore` so we do not need
+ // two separate calls to force the status bar to stay visible.
+ statusBarModeRepositoryStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+ statusBarRequired
+ )
+ statusBarWindowControllerStore.defaultDisplay
+ .setOngoingProcessRequiresStatusBarVisible(statusBarRequired)
+ }
+
+ private fun updateGestureListening(isEnabled: Boolean) {
+ if (isEnabled) {
+ swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+ onStatusBarSwiped()
+ }
+ } else {
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
+ }
+ }
companion object {
private val TAG = "OngoingCall"
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 c2c91b27d074..1a5dcc16f3db 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
@@ -46,5 +46,6 @@ sealed interface OngoingCallModel {
val startTimeMs: Long,
val notificationIconView: StatusBarIconView?,
val intent: PendingIntent?,
+ val notificationKey: String,
) : OngoingCallModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt
new file mode 100644
index 000000000000..c720b1df1e62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.binder
+
+import android.view.View
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
+
+object MobileContentDescriptionViewBinder {
+ fun bind(contentDescription: MobileContentDescription?, view: View) {
+ view.contentDescription =
+ when (contentDescription) {
+ null -> null
+ else -> contentDescription.loadContentDescription(view.context)
+ }
+ }
+}
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 31d349eb4cca..788f041b38c0 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
@@ -29,9 +29,9 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.Flags.statusBarStaticInoutIndicators
-import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.DarkIconDispatcher
@@ -48,12 +48,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBin
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import com.android.app.tracing.coroutines.launchTraced as launch
-private data class Colors(
- @ColorInt val tint: Int,
- @ColorInt val contrast: Int,
-)
+private data class Colors(@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 */
@@ -87,7 +83,7 @@ object MobileIconBinder {
MutableStateFlow(
Colors(
tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
- contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT
+ contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT,
)
)
val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
@@ -105,7 +101,7 @@ object MobileIconBinder {
viewModel.verboseLogger?.logBinderReceivedVisibility(
view,
viewModel.subscriptionId,
- isVisible
+ isVisible,
)
view.isVisible = isVisible
// [StatusIconContainer] can get out of sync sometimes. Make sure to
@@ -152,7 +148,7 @@ object MobileIconBinder {
launch {
viewModel.contentDescription.distinctUntilChanged().collect {
- ContentDescriptionViewBinder.bind(it, view)
+ MobileContentDescriptionViewBinder.bind(it, view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt
new file mode 100644
index 000000000000..84fa07379a49
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.model
+
+import android.annotation.StringRes
+import android.content.Context
+import com.android.systemui.res.R
+
+sealed interface MobileContentDescription {
+ fun loadContentDescription(context: Context): String
+
+ /**
+ * Content description for cellular parameterizes the [networkName] which comes from the system
+ */
+ data class Cellular(val networkName: String, @StringRes val levelDescriptionRes: Int) :
+ MobileContentDescription {
+ override fun loadContentDescription(context: Context): String =
+ context.getString(
+ R.string.accessibility_phone_string_format,
+ networkName,
+ context.getString(levelDescriptionRes),
+ )
+ }
+
+ data class SatelliteContentDescription(@StringRes val resId: Int) : MobileContentDescription {
+ override fun loadContentDescription(context: Context): String =
+ context.getString(this.resId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 103b0e3a6f27..0bd3426712bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import com.android.settingslib.AccessibilityContentDescriptions
import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -28,6 +27,7 @@ import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.Airpla
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.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
@@ -50,7 +50,7 @@ interface MobileIconViewModelCommon {
/** True if this view should be visible at all. */
val isVisible: StateFlow<Boolean>
val icon: Flow<SignalIconModel>
- val contentDescription: Flow<ContentDescription?>
+ val contentDescription: Flow<MobileContentDescription?>
val roaming: Flow<Boolean>
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
val networkTypeIcon: Flow<Icon.Resource?>
@@ -95,10 +95,7 @@ class MobileIconViewModel(
}
private val satelliteProvider by lazy {
- CarrierBasedSatelliteViewModelImpl(
- subscriptionId,
- iconInteractor,
- )
+ CarrierBasedSatelliteViewModelImpl(subscriptionId, iconInteractor)
}
/**
@@ -123,7 +120,7 @@ class MobileIconViewModel(
override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
- override val contentDescription: Flow<ContentDescription?> =
+ override val contentDescription: Flow<MobileContentDescription?> =
vmProvider.flatMapLatest { it.contentDescription }
override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
@@ -154,8 +151,7 @@ private class CarrierBasedSatelliteViewModelImpl(
override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
override val icon: Flow<SignalIconModel> = interactor.signalLevelIcon
- override val contentDescription: Flow<ContentDescription> =
- MutableStateFlow(ContentDescription.Loaded(""))
+ override val contentDescription: Flow<MobileContentDescription?> = MutableStateFlow(null)
/** These fields are not used for satellite icons currently */
override val roaming: Flow<Boolean> = flowOf(false)
@@ -206,27 +202,42 @@ private class CellularIconViewModel(
override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
- override val contentDescription: Flow<ContentDescription?> =
- iconInteractor.signalLevelIcon
- .map {
- // We expect the signal icon to be cellular here since this is the cellular vm
- if (it !is SignalIconModel.Cellular) {
- null
- } else {
- val resId =
- AccessibilityContentDescriptions.getDescriptionForLevel(
- it.level,
- it.numberOfLevels
+ override val contentDescription: Flow<MobileContentDescription?> =
+ combine(iconInteractor.signalLevelIcon, iconInteractor.networkName) { icon, nameModel ->
+ when (icon) {
+ is SignalIconModel.Cellular ->
+ MobileContentDescription.Cellular(
+ nameModel.name,
+ icon.levelDescriptionRes(),
)
- if (resId != 0) {
- ContentDescription.Resource(resId)
- } else {
- null
- }
+ else -> null
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ private fun SignalIconModel.Cellular.levelDescriptionRes() =
+ when (level) {
+ 0 -> R.string.accessibility_no_signal
+ 1 -> R.string.accessibility_one_bar
+ 2 -> R.string.accessibility_two_bars
+ 3 -> R.string.accessibility_three_bars
+ 4 -> {
+ if (numberOfLevels == 6) {
+ R.string.accessibility_four_bars
+ } else {
+ R.string.accessibility_signal_full
+ }
+ }
+ 5 -> {
+ if (numberOfLevels == 6) {
+ R.string.accessibility_signal_full
+ } else {
+ R.string.accessibility_no_signal
+ }
+ }
+ else -> R.string.accessibility_no_signal
+ }
+
private val showNetworkTypeIcon: Flow<Boolean> =
combine(
iconInteractor.isDataConnected,
@@ -248,10 +259,9 @@ private class CellularIconViewModel(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val networkTypeIcon: Flow<Icon.Resource?> =
- combine(
- iconInteractor.networkTypeIconGroup,
- showNetworkTypeIcon,
- ) { networkTypeIconGroup, shouldShow ->
+ combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) {
+ networkTypeIconGroup,
+ shouldShow ->
val desc =
if (networkTypeIconGroup.contentDescription != 0)
ContentDescription.Resource(networkTypeIconGroup.contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 08a98c397d5f..12f578c525fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -161,6 +161,13 @@ constructor(
)
.stateIn(scope, SharingStarted.WhileSubscribed(), true)
+ /** True if any known mobile network is currently using a non terrestrial network */
+ val isAnyConnectionNtn =
+ iconsInteractor.icons.aggregateOver(selector = { it.isNonTerrestrial }, false) {
+ nonTerrestrialNetworks ->
+ nonTerrestrialNetworks.any { it == true }
+ }
+
companion object {
const val TAG = "DeviceBasedSatelliteInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index f3d513940bcf..ea915efb17be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -114,34 +114,39 @@ constructor(
private val showIcon =
if (interactor.isOpportunisticSatelliteIconEnabled) {
- canShowIcon
- .flatMapLatest { canShow ->
- if (!canShow) {
- flowOf(false)
- } else {
- combine(
- shouldShowIconForOosAfterHysteresis,
- interactor.connectionState,
- interactor.isWifiActive,
- airplaneModeRepository.isAirplaneMode,
- ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
- if (isWifiActive || isAirplaneMode) {
- false
- } else {
- showForOos ||
- connectionState == SatelliteConnectionState.On ||
- connectionState == SatelliteConnectionState.Connected
+ canShowIcon
+ .flatMapLatest { canShow ->
+ if (!canShow) {
+ flowOf(false)
+ } else {
+ combine(
+ shouldShowIconForOosAfterHysteresis,
+ interactor.isAnyConnectionNtn,
+ interactor.connectionState,
+ interactor.isWifiActive,
+ airplaneModeRepository.isAirplaneMode,
+ ) { showForOos, anyNtn, connectionState, isWifiActive, isAirplaneMode ->
+ // anyNtn means that there is some mobile network using ntn, and the
+ // mobile icon will show its own satellite icon
+ if (isWifiActive || isAirplaneMode || anyNtn) {
+ false
+ } else {
+ // Show for out of service (which has a hysteresis), or ignore
+ // the hysteresis if we're already connected
+ showForOos ||
+ connectionState == SatelliteConnectionState.On ||
+ connectionState == SatelliteConnectionState.Connected
+ }
}
}
}
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "vm",
- columnName = COL_VISIBLE,
- initialValue = false,
- )
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLog,
+ columnPrefix = "vm",
+ columnName = COL_VISIBLE,
+ initialValue = false,
+ )
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 72df02714d43..d9b2bd1d66c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -20,9 +20,9 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.view.View
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -31,16 +31,19 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ConnectedDisplaysStatusBarNotificationIconViewStore
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
import javax.inject.Inject
+import kotlinx.coroutines.launch
/**
* Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel].
@@ -56,6 +59,7 @@ interface HomeStatusBarViewBinder {
* to support the chip animations.
*/
fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
@@ -65,8 +69,13 @@ interface HomeStatusBarViewBinder {
}
@SysUISingleton
-class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinder {
+class HomeStatusBarViewBinderImpl
+@Inject
+constructor(
+ private val viewStoreFactory: ConnectedDisplaysStatusBarNotificationIconViewStore.Factory
+) : HomeStatusBarViewBinder {
override fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
@@ -75,6 +84,14 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
+ val iconViewStore =
+ if (StatusBarConnectedDisplays.isEnabled) {
+ viewStoreFactory.create(displayId).also {
+ lifecycleScope.launch { it.activate() }
+ }
+ } else {
+ null
+ }
launch {
viewModel.isTransitioningFromLockscreenToOccluded.collect {
listener.onStatusBarVisibilityMaybeChanged()
@@ -102,7 +119,11 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
view.requireViewById(R.id.ongoing_activity_chip_primary)
launch {
viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
- OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
+ OngoingActivityChipBinder.bind(
+ primaryChipModel,
+ primaryChipView,
+ iconViewStore,
+ )
if (StatusBarRootModernization.isEnabled) {
when (primaryChipModel) {
is OngoingActivityChipModel.Shown ->
@@ -142,10 +163,18 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
view.requireViewById(R.id.ongoing_activity_chip_secondary)
launch {
viewModel.ongoingActivityChips.collect { chips ->
- OngoingActivityChipBinder.bind(chips.primary, primaryChipView)
+ OngoingActivityChipBinder.bind(
+ chips.primary,
+ primaryChipView,
+ iconViewStore,
+ )
// TODO(b/364653005): Don't show the secondary chip if there isn't
// enough space for it.
- OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView)
+ OngoingActivityChipBinder.bind(
+ chips.secondary,
+ secondaryChipView,
+ iconViewStore,
+ )
if (StatusBarRootModernization.isEnabled) {
primaryChipView.adjustVisibility(chips.primary.toVisibilityModel())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 812e0eb0908f..5614d82c2e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -176,6 +176,7 @@ fun StatusBarRoot(
// This binder handles everything else
scope.launch {
statusBarViewBinder.bind(
+ context.displayId,
phoneStatusBarView,
statusBarViewModel,
eventAnimationInteractor::animateStatusBarContentForChipEnter,
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 c52275a9eaa4..6e9e1ec7253c 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
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationSt
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor
@@ -235,14 +236,18 @@ constructor(
override val isClockVisible: Flow<VisibilityModel> =
combine(
shouldHomeStatusBarBeVisible,
- headsUpNotificationInteractor.showHeadsUpStatusBar,
+ headsUpNotificationInteractor.statusBarHeadsUpState,
collapsedStatusBarInteractor.visibilityViaDisableFlags,
- ) { shouldStatusBarBeVisible, showHeadsUp, visibilityViaDisableFlags ->
+ ) { shouldStatusBarBeVisible, headsUpState, visibilityViaDisableFlags ->
+ val hideClockForHeadsUp = headsUpState == PinnedStatus.PinnedBySystem
val showClock =
- shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed && !showHeadsUp
+ shouldStatusBarBeVisible &&
+ visibilityViaDisableFlags.isClockAllowed &&
+ !hideClockForHeadsUp
// Always use View.INVISIBLE here, so that animations work
VisibilityModel(showClock.toVisibleOrInvisible(), visibilityViaDisableFlags.animate)
}
+
override val isNotificationIconContainerVisible: Flow<VisibilityModel> =
combine(
shouldHomeStatusBarBeVisible,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 1bb4e8c66ef1..c77f6c1b8552 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -45,5 +45,6 @@ public interface ConfigurationController extends CallbackController<Configuratio
default void onLocaleListChanged() {}
default void onLayoutDirectionChanged(boolean isLayoutRtl) {}
default void onOrientationChanged(int orientation) {}
+ default void onMovedToDisplay(int newDisplayId, Configuration newConfiguration) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 16d5f8d30593..2b0bf1a3d343 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -24,7 +24,6 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.PorterDuff;
@@ -174,11 +173,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mTextWatcher = new SendButtonTextWatcher();
mEditorActionHandler = new EditorActionHandler();
mUiEventLogger = Dependency.get(UiEventLogger.class);
- TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorSurfaceDim,
- });
- mLastBackgroundColor = ta.getColor(0, 0);
- ta.recycle();
+ mLastBackgroundColor = getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceDim);
}
// TODO(b/193539698): move to Controller, since we're just directly accessing a system service
@@ -229,13 +225,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
textColor = mContext.getColorStateList(R.color.remote_input_text);
hintColor = mContext.getColor(R.color.remote_input_hint);
deleteFgColor = textColor.getDefaultColor();
- try (TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
- com.android.internal.R.attr.materialColorSurfaceDim,
- com.android.internal.R.attr.materialColorSurfaceVariant
- })) {
- editBgColor = ta.getColor(0, backgroundColor);
- deleteBgColor = ta.getColor(1, Color.GRAY);
- }
+ editBgColor = getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceDim);
+ deleteBgColor = getContext().getColor(
+ com.android.internal.R.color.materialColorSurfaceVariant);
}
mEditText.setTextColor(textColor);
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 9187e3c43380..d1e807f18196 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
@@ -23,7 +23,6 @@ import android.os.UserManager;
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.settingslib.notification.modes.ZenIconLoader;
-import com.android.systemui.common.ui.GlobalConfig;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
@@ -111,7 +110,7 @@ public interface StatusBarPolicyModule {
* wrong updates in case of secondary displays.
*/
@Binds
- ConfigurationController bindConfigurationController(@GlobalConfig ConfigurationController impl);
+ ConfigurationController bindConfigurationController(@Main ConfigurationController impl);
/** */
@Binds
@@ -189,14 +188,14 @@ public interface StatusBarPolicyModule {
/** */
@Binds
@SysUISingleton
- @GlobalConfig
+ @Main
ConfigurationForwarder provideGlobalConfigurationForwarder(
- @GlobalConfig ConfigurationController configurationController);
+ @Main ConfigurationController configurationController);
/** */
@Provides
@SysUISingleton
- @GlobalConfig
+ @Main
static ConfigurationController provideGlobalConfigurationController(
@Application Context context, ConfigurationControllerImpl.Factory factory) {
return factory.create(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index 9839f9d76537..12ed647fdee7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -40,12 +40,16 @@ import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
import java.time.Duration
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* An interactor that performs business logic related to the status and configuration of Zen Mode
@@ -58,6 +62,7 @@ constructor(
private val zenModeRepository: ZenModeRepository,
private val notificationSettingsRepository: NotificationSettingsRepository,
@Background private val bgDispatcher: CoroutineDispatcher,
+ @Background private val backgroundScope: CoroutineScope,
private val iconLoader: ZenIconLoader,
deviceProvisioningRepository: DeviceProvisioningRepository,
userSetupRepository: UserSetupRepository,
@@ -101,13 +106,16 @@ constructor(
/**
* Returns the special "manual DND" mode.
*
- * This is only meant as a temporary solution for "legacy" UI pieces that handle DND
- * specifically; any new or migrated features should use modes more generally, through [modes]
- * or [activeModes].
+ * This should only be used when there is a strong reason to handle DND specifically (such as
+ * legacy UI pieces that haven't been updated to use modes more generally, or if the user
+ * explicitly wants a shortcut to DND). Please prefer using [modes] or [activeModes] in all
+ * other scenarios.
*/
- val dndMode: Flow<ZenMode?> by lazy {
+ val dndMode: StateFlow<ZenMode?> by lazy {
ModesUi.assertInNewMode()
- zenModeRepository.modes.map { modes -> modes.singleOrNull { it.isManualDnd } }
+ zenModeRepository.modes
+ .map { modes -> modes.singleOrNull { it.isManualDnd } }
+ .stateIn(scope = backgroundScope, started = SharingStarted.Eagerly, initialValue = null)
}
/** Flow returning the currently active mode(s), if any. */
@@ -201,6 +209,14 @@ constructor(
zenModeRepository.deactivateMode(zenMode)
}
+ fun deactivateAllModes() {
+ for (mode in zenModeRepository.getModes()) {
+ if (mode.isActive) {
+ deactivateMode(mode)
+ }
+ }
+ }
+
private val zenDuration
get() = notificationSettingsRepository.zenDuration.value
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index fe1d64736991..6175ea190697 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/**
@@ -59,7 +60,7 @@ constructor(
private val showingHeadsUpStatusBar: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- headsUpNotificationInteractor.showHeadsUpStatusBar
+ headsUpNotificationInteractor.statusBarHeadsUpState.map { it.isPinned }
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 7475388e80c2..0a10b440644d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar
import android.os.VibrationEffect
import android.view.View
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
import com.android.internal.logging.InstanceId
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
@@ -52,8 +52,8 @@ data class ChipbarInfo(
) : TemporaryViewInfo() {
companion object {
// LINT.IfChange
- @AttrRes val DEFAULT_ICON_TINT = com.android.internal.R.attr.materialColorOnSecondaryFixed
- // LINT.ThenChange(systemui/res/layout/chipbar.xml)
+ @ColorRes val DEFAULT_ICON_TINT = com.android.internal.R.color.materialColorOnSecondaryFixed
+ // LINT.ThenChange(/packages/SystemUI/res/layout/chipbar.xml)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index bfdae626b5e8..e1cc11a7cfd1 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -45,6 +45,8 @@ fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni
bodyResId = R.string.touchpad_back_gesture_guidance,
titleSuccessResId = R.string.touchpad_back_gesture_success_title,
bodySuccessResId = R.string.touchpad_back_gesture_success_body,
+ titleErrorResId = R.string.gesture_error_title,
+ bodyErrorResId = R.string.touchpad_back_gesture_error_body,
),
animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_back_edu),
)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 2332c0051c69..ed84f9c42c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -54,6 +54,8 @@ sealed interface GestureUiState {
val progressStartMarker: String,
val progressEndMarker: String,
) : GestureUiState
+
+ data object Error : GestureUiState
}
fun GestureState.toGestureUiState(
@@ -66,19 +68,31 @@ fun GestureState.toGestureUiState(
is GestureState.InProgress ->
GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker)
is GestureState.Finished -> GestureUiState.Finished(successAnimation)
+ GestureState.Error -> GestureUiState.Error
}
}
-fun GestureUiState.toTutorialActionState(): TutorialActionState {
+fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState {
return when (this) {
NotStarted -> TutorialActionState.NotStarted
- is GestureUiState.InProgress ->
- TutorialActionState.InProgress(
- progress = progress,
- startMarker = progressStartMarker,
- endMarker = progressEndMarker,
- )
+ is GestureUiState.InProgress -> {
+ val inProgress =
+ TutorialActionState.InProgress(
+ progress = progress,
+ startMarker = progressStartMarker,
+ endMarker = progressEndMarker,
+ )
+ if (
+ previousState is TutorialActionState.InProgressAfterError ||
+ previousState is TutorialActionState.Error
+ ) {
+ return TutorialActionState.InProgressAfterError(inProgress)
+ } else {
+ return inProgress
+ }
+ }
is Finished -> TutorialActionState.Finished(successAnimation)
+ GestureUiState.Error -> TutorialActionState.Error
}
}
@@ -102,11 +116,11 @@ fun GestureTutorialScreen(
easterEggTriggered,
resetEasterEggFlag = { easterEggTriggered = false },
) {
- ActionTutorialContent(
- gestureState.toTutorialActionState(),
- onDoneButtonClicked,
- screenConfig,
- )
+ var lastState: TutorialActionState by remember {
+ mutableStateOf(TutorialActionState.NotStarted)
+ }
+ lastState = gestureState.toTutorialActionState(lastState)
+ ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index 45fdd21e795e..26604ca6b845 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -42,6 +42,8 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni
bodyResId = R.string.touchpad_home_gesture_guidance,
titleSuccessResId = R.string.touchpad_home_gesture_success_title,
bodySuccessResId = R.string.touchpad_home_gesture_success_body,
+ titleErrorResId = R.string.gesture_error_title,
+ bodyErrorResId = R.string.touchpad_home_gesture_error_body,
),
animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_home_edu),
)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index 680b670f4d97..6400aca57693 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -42,6 +42,8 @@ fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: ()
bodyResId = R.string.touchpad_recent_apps_gesture_guidance,
titleSuccessResId = R.string.touchpad_recent_apps_gesture_success_title,
bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body,
+ titleErrorResId = R.string.gesture_error_title,
+ bodyErrorResId = R.string.touchpad_recent_gesture_error_body,
),
animations =
TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_recent_apps_edu),
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
index f27ddb515c24..7bc7e81ceb51 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
@@ -23,6 +23,8 @@ sealed interface GestureState {
data class InProgress(val progress: Float = 0f, val direction: GestureDirection? = null) :
GestureState
+
+ data object Error : GestureState
}
enum class GestureDirection {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
index 24f5d1f00794..69886e4a1242 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
@@ -28,7 +28,7 @@ inline fun updateGestureState(
if (isFinished(gestureState)) {
gestureStateChangedCallback(GestureState.Finished)
} else {
- gestureStateChangedCallback(GestureState.NotStarted)
+ gestureStateChangedCallback(GestureState.Error)
}
}
is Moving -> {
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
index 6160b00379ef..5b48c1ff628c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
@@ -35,27 +35,21 @@ class ViewUtil @Inject constructor() {
fun touchIsWithinView(view: View, x: Float, y: Float): Boolean {
val left = view.locationOnScreen[0]
val top = view.locationOnScreen[1]
- return left <= x &&
- x <= left + view.width &&
- top <= y &&
- y <= top + view.height
+ return left <= x && x <= left + view.width && top <= y && y <= top + view.height
}
- /**
- * Sets [outRect] to be the view's location within its window.
- */
- fun setRectToViewWindowLocation(view: View, outRect: Rect) {
- val locInWindow = IntArray(2)
- view.getLocationInWindow(locInWindow)
-
- val x = locInWindow[0]
- val y = locInWindow[1]
-
- outRect.set(
- x,
- y,
- x + view.width,
- y + view.height,
- )
- }
+ /** Sets [outRect] to be the view's location within its window. */
+ fun setRectToViewWindowLocation(view: View, outRect: Rect) = view.viewBoundsOnScreen(outRect)
+}
+
+fun View.viewBoundsOnScreen(outRect: Rect) {
+ val locInWindow = IntArray(2)
+ getLocationInWindow(locInWindow)
+
+ val x = locInWindow[0]
+ val y = locInWindow[1]
+
+ outRect.set(x, y, x + width, y + height)
}
+
+fun View.viewBoundsOnScreen(): Rect = Rect().also { viewBoundsOnScreen(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index b83613ba4f8c..40719185e290 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -20,7 +20,6 @@ import android.media.AudioManager
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
-import android.provider.Settings
import com.android.settingslib.volume.data.repository.AudioSystemRepository
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.plugins.VolumeDialogController
@@ -66,11 +65,6 @@ constructor(
}
},
currentRingerMode = RingerMode(state.ringerModeInternal),
- isEnabled =
- !(state.zenMode == Settings.Global.ZEN_MODE_ALARMS ||
- state.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS ||
- (state.zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS &&
- state.disallowRinger)),
isMuted = it.level == 0 || it.muted,
level = it.level,
levelMax = it.levelMax,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
index 3c24e02f3732..84a82805aace 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
@@ -23,8 +23,6 @@ data class VolumeDialogRingerModel(
val availableModes: List<RingerMode>,
/** Current ringer mode internal */
val currentRingerMode: RingerMode,
- /** whether the ringer is allowed given the current ZenMode */
- val isEnabled: Boolean,
/** Whether the current ring stream level is zero or the controller state is muted */
val isMuted: Boolean,
/** Ring stream level */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index f98ad45f30bc..f04fb2c3b8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -30,10 +30,10 @@ import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.R as internalR
-import com.android.settingslib.Utils
import com.android.systemui.res.R
import com.android.systemui.util.children
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.util.VolumeDialogRingerDrawerTransitionListener
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonUiModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
@@ -42,6 +42,7 @@ import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModelSta
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import javax.inject.Inject
+import kotlin.properties.Delegates
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.launchIn
@@ -71,6 +72,27 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
val drawerContainer = view.requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
val unselectedButtonUiModel = RingerButtonUiModel.getUnselectedButton(view.context)
val selectedButtonUiModel = RingerButtonUiModel.getSelectedButton(view.context)
+ val volumeDialogBgSmallRadius =
+ view.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_background_square_corner_radius
+ )
+ val volumeDialogBgFullRadius =
+ view.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_background_corner_radius
+ )
+ var backgroundAnimationProgress: Float by
+ Delegates.observable(0F) { _, _, progress ->
+ volumeDialogBackgroundView.applyCorners(
+ fullRadius = volumeDialogBgFullRadius,
+ diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
+ progress,
+ )
+ }
+ val ringerDrawerTransitionListener = VolumeDialogRingerDrawerTransitionListener {
+ backgroundAnimationProgress = it
+ }
+ drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
+ volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
viewModel.ringerViewModel
.onEach { ringerState ->
when (ringerState) {
@@ -87,10 +109,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
selectedButtonUiModel,
unselectedButtonUiModel,
)
+ ringerDrawerTransitionListener.setProgressChangeEnabled(true)
drawerContainer.closeDrawer(uiModel.currentButtonIndex)
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background
- )
}
is RingerDrawerState.Closed -> {
@@ -103,11 +123,31 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
uiModel,
selectedButtonUiModel,
unselectedButtonUiModel,
+ onProgressChanged = { progress, isReverse ->
+ // Let's make button progress when switching matches
+ // motionLayout transition progress. When full radius,
+ // progress is 0.0. When small radius, progress is 1.0.
+ backgroundAnimationProgress =
+ if (isReverse) {
+ 1F - progress
+ } else {
+ progress
+ }
+ },
) {
+ if (
+ uiModel.currentButtonIndex ==
+ uiModel.availableButtons.size - 1
+ ) {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(
+ false
+ )
+ } else {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(
+ true
+ )
+ }
drawerContainer.closeDrawer(uiModel.currentButtonIndex)
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background
- )
}
}
}
@@ -120,16 +160,18 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
unselectedButtonUiModel,
)
// Open drawer
- drawerContainer.transitionToState(
- R.id.volume_dialog_ringer_drawer_open
- )
if (
- uiModel.currentButtonIndex != uiModel.availableButtons.size - 1
+ uiModel.currentButtonIndex == uiModel.availableButtons.size - 1
) {
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background_small_radius
- )
+ ringerDrawerTransitionListener.setProgressChangeEnabled(false)
+ } else {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(true)
}
+ drawerContainer.transitionToState(
+ R.id.volume_dialog_ringer_drawer_open
+ )
+ volumeDialogBackgroundView.background =
+ volumeDialogBackgroundView.background.mutate()
}
}
}
@@ -150,6 +192,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
uiModel: RingerViewModel,
selectedButtonUiModel: RingerButtonUiModel,
unselectedButtonUiModel: RingerButtonUiModel,
+ onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
onAnimationEnd: Runnable? = null,
) {
ensureChildCount(R.layout.volume_ringer_button, uiModel.availableButtons.size)
@@ -177,10 +220,26 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
CLOSE_DRAWER_DELAY,
)
}
-
- // We only need to execute on roundness animation end once.
- selectedButton.animateTo(selectedButtonUiModel, roundnessAnimationEndListener)
- unselectedButton.animateTo(unselectedButtonUiModel)
+ // We only need to execute on roundness animation end and volume dialog background
+ // progress update once because these changes should be applied once on volume dialog
+ // background and ringer drawer views.
+ selectedButton.animateTo(
+ selectedButtonUiModel,
+ if (uiModel.currentButtonIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ roundnessAnimationEndListener,
+ )
+ unselectedButton.animateTo(
+ unselectedButtonUiModel,
+ if (previousIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ )
} else {
bindButtons(viewModel, uiModel, onAnimationEnd)
}
@@ -196,15 +255,17 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
ringerButton?.let {
val view = getChildAt(count - index - 1)
+ val isOpen = uiModel.drawerState is RingerDrawerState.Open
if (index == uiModel.currentButtonIndex) {
view.bindDrawerButton(
- uiModel.selectedButton,
+ if (isOpen) it else uiModel.selectedButton,
viewModel,
+ isOpen,
isSelected = true,
isAnimated = isAnimated,
)
} else {
- view.bindDrawerButton(it, viewModel, isAnimated)
+ view.bindDrawerButton(it, viewModel, isOpen, isAnimated = isAnimated)
}
}
}
@@ -214,23 +275,29 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
private fun View.bindDrawerButton(
buttonViewModel: RingerButtonViewModel,
viewModel: VolumeDialogRingerDrawerViewModel,
+ isOpen: Boolean,
isSelected: Boolean = false,
isAnimated: Boolean = false,
) {
+ val ringerContentDesc = context.getString(buttonViewModel.contentDescriptionResId)
with(requireViewById<ImageButton>(R.id.volume_drawer_button)) {
setImageResource(buttonViewModel.imageResId)
- contentDescription = context.getString(buttonViewModel.contentDescriptionResId)
+ contentDescription =
+ if (isSelected && !isOpen) {
+ context.getString(
+ R.string.volume_ringer_drawer_closed_content_description,
+ ringerContentDesc,
+ )
+ } else {
+ ringerContentDesc
+ }
if (isSelected && !isAnimated) {
setBackgroundResource(R.drawable.volume_drawer_selection_bg)
- setColorFilter(
- Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnPrimary)
- )
+ setColorFilter(context.getColor(internalR.color.materialColorOnPrimary))
background = background.mutate()
} else if (!isAnimated) {
setBackgroundResource(R.drawable.volume_ringer_item_bg)
- setColorFilter(
- Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnSurface)
- )
+ setColorFilter(context.getColor(internalR.color.materialColorOnSurface))
background = background.mutate()
}
setOnClickListener {
@@ -354,6 +421,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
private suspend fun ImageButton.animateTo(
ringerButtonUiModel: RingerButtonUiModel,
+ onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
roundnessAnimationEndListener: DynamicAnimation.OnAnimationEndListener? = null,
) {
val roundnessAnimation =
@@ -364,6 +432,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
ringerButtonUiModel.cornerRadius - (background as GradientDrawable).cornerRadius
val roundnessAnimationUpdateListener =
DynamicAnimation.OnAnimationUpdateListener { _, value, _ ->
+ onProgressChanged(value, cornerRadiusDiff > 0F)
(background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff
background.invalidateSelf()
}
@@ -394,4 +463,9 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
)
}
}
+
+ private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float) {
+ (background as GradientDrawable).cornerRadius = fullRadius - progress * diff
+ background.invalidateSelf()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt
new file mode 100644
index 000000000000..6e3db0afb483
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.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.systemui.volume.dialog.ringer.ui.util
+
+import androidx.constraintlayout.motion.widget.MotionLayout
+
+class VolumeDialogRingerDrawerTransitionListener(private val onProgressChanged: (Float) -> Unit) :
+ MotionLayout.TransitionListener {
+
+ private var notifyProgressChangeEnabled = true
+
+ fun setProgressChangeEnabled(enabled: Boolean) {
+ notifyProgressChangeEnabled = enabled
+ }
+
+ override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {}
+
+ override fun onTransitionChange(
+ motionLayout: MotionLayout?,
+ startId: Int,
+ endId: Int,
+ progress: Float,
+ ) {
+ if (notifyProgressChangeEnabled) {
+ onProgressChanged(progress)
+ }
+ }
+
+ override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {}
+
+ override fun onTransitionTrigger(
+ motionLayout: MotionLayout?,
+ triggerId: Int,
+ positive: Boolean,
+ progress: Float,
+ ) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonUiModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonUiModel.kt
index 3c465674ebb5..832f1c3471d6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonUiModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonUiModel.kt
@@ -31,30 +31,20 @@ data class RingerButtonUiModel(
companion object {
fun getUnselectedButton(context: Context): RingerButtonUiModel {
return RingerButtonUiModel(
- tintColor =
- Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnSurface),
- backgroundColor =
- Utils.getColorAttrDefaultColor(
- context,
- internalR.attr.materialColorSurfaceContainerHighest,
- ),
- cornerRadius =
- context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_background_square_corner_radius
- ),
+ tintColor = context.getColor(internalR.color.materialColorOnSurface),
+ backgroundColor = context.getColor(
+ internalR.color.materialColorSurfaceContainerHighest),
+ cornerRadius = context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_background_square_corner_radius),
)
}
fun getSelectedButton(context: Context): RingerButtonUiModel {
return RingerButtonUiModel(
- tintColor =
- Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnPrimary),
- backgroundColor =
- Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorPrimary),
- cornerRadius =
- context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_selected_button_background_radius
- ),
+ tintColor = context.getColor(internalR.color.materialColorOnPrimary),
+ backgroundColor = context.getColor(internalR.color.materialColorPrimary),
+ cornerRadius = context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_selected_button_background_radius),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index e646636dd2a2..627d75ee108d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -21,10 +21,13 @@ import android.media.AudioAttributes
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.media.AudioManager.STREAM_RING
import android.os.VibrationEffect
import android.widget.Toast
import com.android.internal.R as internalR
import com.android.settingslib.Utils
+import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -57,7 +60,8 @@ constructor(
@Application private val applicationContext: Context,
@VolumeDialog private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val interactor: VolumeDialogRingerInteractor,
+ soundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ private val ringerInteractor: VolumeDialogRingerInteractor,
private val vibrator: VibratorHelper,
private val volumeDialogLogger: VolumeDialogLogger,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@@ -66,10 +70,14 @@ constructor(
private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
val ringerViewModel: StateFlow<RingerViewModelState> =
- combine(interactor.ringerModel, drawerState) { ringerModel, state ->
+ combine(
+ soundPolicyInteractor.isZenMuted(AudioStream(STREAM_RING)),
+ ringerInteractor.ringerModel,
+ drawerState,
+ ) { isZenMuted, ringerModel, state ->
level = ringerModel.level
levelMax = ringerModel.levelMax
- ringerModel.toViewModel(state)
+ ringerModel.toViewModel(state, isZenMuted)
}
.flowOn(backgroundDispatcher)
.stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
@@ -90,7 +98,7 @@ constructor(
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
provideTouchFeedback(ringerMode)
maybeShowToast(ringerMode)
- interactor.setRingerMode(ringerMode)
+ ringerInteractor.setRingerMode(ringerMode)
}
visibilityInteractor.resetDismissTimeout()
drawerState.value =
@@ -113,7 +121,7 @@ constructor(
private fun provideTouchFeedback(ringerMode: RingerMode) {
when (ringerMode.value) {
RINGER_MODE_NORMAL -> {
- interactor.scheduleTouchFeedback()
+ ringerInteractor.scheduleTouchFeedback()
null
}
RINGER_MODE_SILENT -> VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
@@ -123,7 +131,8 @@ constructor(
}
private fun VolumeDialogRingerModel.toViewModel(
- drawerState: RingerDrawerState
+ drawerState: RingerDrawerState,
+ isZenMuted: Boolean,
): RingerViewModelState {
val currentIndex = availableModes.indexOf(currentRingerMode)
if (currentIndex == -1) {
@@ -132,10 +141,11 @@ constructor(
return if (currentIndex == -1 || isSingleVolume) {
RingerViewModelState.Unavailable
} else {
- toButtonViewModel(currentRingerMode, isSelectedButton = true)?.let {
+ toButtonViewModel(currentRingerMode, isZenMuted, isSelectedButton = true)?.let {
RingerViewModelState.Available(
RingerViewModel(
- availableButtons = availableModes.map { mode -> toButtonViewModel(mode) },
+ availableButtons =
+ availableModes.map { mode -> toButtonViewModel(mode, isZenMuted) },
currentButtonIndex = currentIndex,
selectedButton = it,
drawerState = drawerState,
@@ -147,6 +157,7 @@ constructor(
private fun VolumeDialogRingerModel.toButtonViewModel(
ringerMode: RingerMode,
+ isZenMuted: Boolean,
isSelectedButton: Boolean = false,
): RingerButtonViewModel? {
return when (ringerMode.value) {
@@ -176,7 +187,7 @@ constructor(
)
RINGER_MODE_NORMAL ->
when {
- isMuted && isEnabled ->
+ isMuted && !isZenMuted ->
RingerButtonViewModel(
imageResId =
if (isSelectedButton) {
@@ -226,7 +237,7 @@ constructor(
private fun maybeShowToast(ringerMode: RingerMode) {
coroutineScope.launch {
- val seenToastCount = interactor.getToastCount()
+ val seenToastCount = ringerInteractor.getToastCount()
if (seenToastCount > SHOW_RINGER_TOAST_COUNT) {
return@launch
}
@@ -260,7 +271,7 @@ constructor(
)
}
toastText?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show() }
- interactor.updateToastCount(seenToastCount)
+ ringerInteractor.updateToastCount(seenToastCount)
}
}
}
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 e52bad9c39bf..f30524638150 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
@@ -18,11 +18,12 @@ package com.android.systemui.volume.dialog.sliders.ui
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.annotation.SuppressLint
import android.view.View
import android.view.animation.DecelerateInterpolator
import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
@@ -48,24 +49,27 @@ constructor(
val sliderView: Slider =
view.requireViewById<Slider>(R.id.volume_dialog_slider).apply {
labelBehavior = LabelFormatter.LABEL_GONE
+ trackIconActiveColor = trackInactiveTintList
}
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
- viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
+ viewModel.state.onEach { it.bindToSlider(sliderView) }.launchIn(this)
}
- private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private suspend fun VolumeDialogSliderStateModel.bindToSlider(slider: Slider) {
with(slider) {
- valueFrom = levelMin.toFloat()
- valueTo = levelMax.toFloat()
+ valueFrom = minValue
+ valueTo = maxValue
// coerce the current value to the new value range before animating it
value = value.coerceIn(valueFrom, valueTo)
setValueAnimated(
- level.toFloat(),
+ value,
jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
)
+ trackIconActiveEnd = context.getDrawable(iconRes)
}
}
}
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
new file mode 100644
index 000000000000..5c39b6f9359c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.volume.dialog.sliders.ui.viewmodel
+
+import android.media.AudioManager
+import androidx.annotation.DrawableRes
+import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
+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.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+
+class VolumeDialogSliderIconProvider
+@Inject
+constructor(
+ private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ private val audioVolumeInteractor: AudioVolumeInteractor,
+) {
+
+ @DrawableRes
+ fun getStreamIcon(
+ stream: Int,
+ level: Int,
+ levelMin: Int,
+ levelMax: Int,
+ isMuted: Boolean,
+ isRoutedToBluetooth: Boolean,
+ ): Flow<Int> {
+ return combine(
+ notificationsSoundPolicyInteractor.isZenMuted(AudioStream(stream)),
+ ringerModeForStream(stream),
+ ) { isZenMuted, ringerMode ->
+ val isStreamOffline = level == 0 || isMuted
+ if (isZenMuted) {
+ // TODO(b/372466264) use icon for the corresponding zenmode
+ return@combine com.android.internal.R.drawable.ic_qs_dnd
+ }
+ when (ringerMode?.value) {
+ AudioManager.RINGER_MODE_VIBRATE ->
+ return@combine R.drawable.ic_volume_ringer_vibrate
+ AudioManager.RINGER_MODE_SILENT -> return@combine R.drawable.ic_ring_volume_off
+ }
+ if (isRoutedToBluetooth) {
+ return@combine if (stream == AudioManager.STREAM_VOICE_CALL) {
+ R.drawable.ic_volume_bt_sco
+ } else {
+ if (isStreamOffline) {
+ R.drawable.ic_volume_media_bt_mute
+ } else {
+ R.drawable.ic_volume_media_bt
+ }
+ }
+ }
+
+ return@combine if (isStreamOffline) {
+ getMutedIconForStream(stream) ?: getIconForStream(stream)
+ } else {
+ if (level < (levelMax + levelMin) / 2) {
+ // This icon is different on TV
+ R.drawable.ic_volume_media_low
+ } else {
+ getIconForStream(stream)
+ }
+ }
+ }
+ }
+
+ @DrawableRes
+ private fun getMutedIconForStream(stream: Int): Int? {
+ return when (stream) {
+ AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
+ AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
+ AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
+ else -> null
+ }
+ }
+
+ @DrawableRes
+ private fun getIconForStream(stream: Int): Int {
+ return when (stream) {
+ AudioManager.STREAM_ACCESSIBILITY -> R.drawable.ic_volume_accessibility
+ AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media
+ AudioManager.STREAM_RING -> R.drawable.ic_ring_volume
+ AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer
+ AudioManager.STREAM_ALARM -> R.drawable.ic_alarm
+ AudioManager.STREAM_VOICE_CALL -> com.android.internal.R.drawable.ic_phone
+ AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system
+ else -> error("Unsupported stream: $stream")
+ }
+ }
+
+ /**
+ * Emits [RingerMode] for the [stream] if it's affecting it and null when [RingerMode] doesn't
+ * affect the [stream]
+ */
+ private fun ringerModeForStream(stream: Int): Flow<RingerMode?> {
+ return if (stream == AudioManager.STREAM_RING) {
+ audioVolumeInteractor.ringerMode
+ } else {
+ flowOf(null)
+ }
+ }
+}
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
new file mode 100644
index 000000000000..5750c049082f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.volume.dialog.sliders.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+
+data class VolumeDialogSliderStateModel(
+ val minValue: Float,
+ val maxValue: Float,
+ val value: Float,
+ @DrawableRes val iconRes: Int,
+)
+
+fun VolumeDialogStreamModel.toStateModel(@DrawableRes iconRes: Int): VolumeDialogSliderStateModel {
+ return VolumeDialogSliderStateModel(
+ minValue = levelMin.toFloat(),
+ value = level.toFloat(),
+ maxValue = levelMax.toFloat(),
+ iconRes = iconRes,
+ )
+}
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 6dd5b638a3bc..2d5652420ec8 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
@@ -32,7 +32,9 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
@@ -56,12 +58,12 @@ constructor(
private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
+ private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
private val systemClock: SystemClock,
) {
private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
-
- val model: Flow<VolumeDialogStreamModel> =
+ private val model: Flow<VolumeDialogStreamModel> =
interactor.slider
.filter {
val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
@@ -70,6 +72,21 @@ constructor(
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
+ val state: Flow<VolumeDialogSliderStateModel> =
+ model.flatMapLatest { streamModel ->
+ with(streamModel) {
+ volumeDialogSliderIconProvider.getStreamIcon(
+ stream = stream,
+ level = level,
+ levelMin = levelMin,
+ levelMax = levelMax,
+ isMuted = muted,
+ isRoutedToBluetooth = routedToBluetooth,
+ )
+ }
+ .map { icon -> streamModel.toStateModel(icon) }
+ }
+
init {
userVolumeUpdates
.filterNotNull()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index e565de5c55ea..6c4a853b42ad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -17,7 +17,9 @@
package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
import android.content.Context
+import android.graphics.Color as GraphicsColor
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
@@ -77,7 +79,13 @@ constructor(
ConnectedDeviceViewModel(
label = label,
labelColor =
- Color.Attribute(com.android.internal.R.attr.materialColorOnSurfaceVariant),
+ if (Flags.volumeRedesign()) {
+ Color.Resource(com.android.internal.R.color.materialColorOnSurface)
+ } else {
+ Color.Resource(
+ com.android.internal.R.color.materialColorOnSurfaceVariant
+ )
+ },
deviceName =
if (mediaOutputModel.isInAudioSharing) {
context.getString(R.string.audio_sharing_description)
@@ -88,19 +96,15 @@ constructor(
},
deviceNameColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorOnSurface)
+ Color.Resource(com.android.internal.R.color.materialColorOnSurface)
} else {
- Color.Attribute(
- com.android.internal.R.attr.materialColorOnSurfaceVariant
+ Color.Resource(
+ com.android.internal.R.color.materialColorOnSurfaceVariant
)
},
)
}
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- null,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
mediaOutputComponentInteractor.mediaOutputModel
@@ -121,17 +125,33 @@ constructor(
icon = icon,
iconColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+ if (Flags.volumeRedesign()) {
+ Color.Resource(
+ com.android.internal.R.color.materialColorOnPrimary
+ )
+ } else {
+ Color.Resource(
+ com.android.internal.R.color.materialColorSurface
+ )
+ }
} else {
- Color.Attribute(
- com.android.internal.R.attr.materialColorSurfaceContainerHighest
+ Color.Resource(
+ com.android.internal.R.color.materialColorSurfaceContainerHighest
)
},
backgroundColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSecondary)
+ if (Flags.volumeRedesign()) {
+ Color.Resource(
+ com.android.internal.R.color.materialColorPrimary
+ )
+ } else {
+ Color.Resource(
+ com.android.internal.R.color.materialColorSecondary
+ )
+ }
} else {
- Color.Attribute(com.android.internal.R.attr.materialColorOutline)
+ Color.Resource(com.android.internal.R.color.materialColorOutline)
},
)
} else {
@@ -139,38 +159,29 @@ constructor(
icon = icon,
iconColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- )
+ if (Flags.volumeRedesign()) {
+ Color.Resource(
+ com.android.internal.R.color.materialColorPrimary
+ )
+ } else {
+ Color.Resource(
+ com.android.internal.R.color.materialColorOnSurfaceVariant
+ )
+ }
} else {
- Color.Attribute(com.android.internal.R.attr.materialColorOutline)
- },
- backgroundColor =
- if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSurface)
- } else {
- Color.Attribute(
- com.android.internal.R.attr.materialColorSurfaceContainerHighest
- )
+ Color.Resource(com.android.internal.R.color.materialColorOutline)
},
+ backgroundColor = Color.Loaded(GraphicsColor.TRANSPARENT),
)
}
}
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- null,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
val enabled: StateFlow<Boolean> =
mediaOutputComponentInteractor.mediaOutputModel
.filterData()
.map { it.canOpenAudioSwitcher }
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- true,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, true)
fun onBarClick(expandable: Expandable?) {
uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
@@ -178,7 +189,7 @@ constructor(
mediaOutputComponentInteractor.mediaOutputModel.value
actionsInteractor.onBarClick(
(result as? Result.Data<MediaOutputComponentModel>)?.data,
- expandable
+ expandable,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt b/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt
new file mode 100644
index 000000000000..8b6c8601f5d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.window.flag
+
+import com.android.systemui.Flags
+
+/**
+ * Flag that controls whether the background surface is blurred or not while on the
+ * lockscreen/shade/bouncer. This makes the background of scrim, bouncer and few other opaque
+ * surfaces transparent so that we can see the blur effect on the background surface (wallpaper).
+ */
+object WindowBlurFlag {
+ /** Whether the blur is enabled or not */
+ @JvmStatic
+ val isEnabled
+ // Add flags here that require scrims/background surfaces to be transparent.
+ get() = Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 073781e6101d..0ec71c21985e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -48,7 +48,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.CoreStartable;
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -60,6 +59,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.wm.shell.dagger.WMComponent;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.onehanded.OneHanded;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index ae9454498089..a41725f754df 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -90,6 +90,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.clearInvocations
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -528,6 +529,8 @@ class ClockEventControllerTest : SysuiTestCase() {
fun listenForDnd_onDndChange_updatesClockZenMode() =
testScope.runTest {
underTest.listenForDnd(testScope.backgroundScope)
+ runCurrent()
+ clearInvocations(events)
zenModeRepository.activateMode(dndModeId)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 50d0049dbcd7..dddaabb66022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,10 +26,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.PointF;
+
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
@@ -49,6 +51,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -69,13 +72,17 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+ secureSettings);
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
secureSettings));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index f4580c173579..400b3b388c31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -159,7 +159,8 @@ public class MenuViewLayerTest extends SysuiTestCase {
new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
- mMenuViewModel = new MenuViewModel(mSpyContext, mSecureSettings);
+ mMenuViewModel = new MenuViewModel(
+ mSpyContext, mStubAccessibilityManager, mSecureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
mSpyContext, mStubWindowManager);
mMenuView = spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 7c0892891707..11199cb2f34a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -44,6 +44,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.AudioRecordingConfiguration;
@@ -593,11 +594,11 @@ public class AppOpsControllerTest extends SysuiTestCase {
//untrusted receiver access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER, TEST_CHAIN_ID);
//untrusted intermediary access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY, TEST_CHAIN_ID);
assertTrue(mController.getActiveAppOps().isEmpty());
}
@@ -606,11 +607,11 @@ public class AppOpsControllerTest extends SysuiTestCase {
public void testTrustedChainUsagesKept() {
//untrusted accessor access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, TEST_CHAIN_ID);
//trusted access
mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER | AppOpsManager.ATTRIBUTION_FLAG_TRUSTED,
TEST_CHAIN_ID);
assertEquals(2, mController.getActiveAppOps().size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index f5616d45a3da..7fb74b3439bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -51,11 +51,11 @@ class ControlsFavoritingActivityTest : SysuiTestCase() {
private companion object {
val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+ val TEST_STRUCTURE: CharSequence = "TestStructure"
val TEST_CONTROL =
mock(Control::class.java, Answers.RETURNS_MOCKS)!!.apply {
whenever(structure).thenReturn(TEST_STRUCTURE)
}
- val TEST_STRUCTURE: CharSequence = "TestStructure"
val TEST_APP: CharSequence = "TestApp"
private fun View.waitForPost() {
@@ -158,7 +158,7 @@ class ControlsFavoritingActivityTest : SysuiTestCase() {
assertThat(getBooleanExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, false))
.isTrue()
assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_STRUCTURE))
- .isEqualTo("")
+ .isEqualTo(TEST_STRUCTURE)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
new file mode 100644
index 000000000000..d7fcb6a4c2a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -0,0 +1,471 @@
+/*
+ * Copyright 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.education.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
+import com.android.systemui.contextualeducation.GestureType.OVERVIEW
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.education.data.model.GestureEduModel
+import com.android.systemui.education.data.repository.contextualEducationRepository
+import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
+import com.android.systemui.keyboard.data.repository.keyboardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.touchpadRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class KeyboardTouchpadEduInteractorParameterizedTest(private val gestureType: GestureType) :
+ SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val contextualEduInteractor = kosmos.contextualEducationInteractor
+ private val repository = kosmos.contextualEducationRepository
+ private val touchpadRepository = kosmos.touchpadRepository
+ private val keyboardRepository = kosmos.keyboardRepository
+ private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
+ private val userRepository = kosmos.fakeUserRepository
+ private val overviewProxyService = kosmos.mockOverviewProxyService
+
+ private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
+ private val eduClock = kosmos.fakeEduClock
+ private val minDurationForNextEdu =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
+ private val initialDelayElapsedDuration =
+ KeyboardTouchpadEduInteractor.initialDelayDuration + 1.seconds
+
+ @Before
+ fun setup() {
+ underTest.start()
+ contextualEduInteractor.start()
+ userRepository.setUserInfos(USER_INFOS)
+ testScope.launch {
+ contextualEduInteractor.updateKeyboardFirstConnectionTime()
+ contextualEduInteractor.updateTouchpadFirstConnectionTime()
+ }
+ }
+
+ @Test
+ fun newEducationInfoOnMaxSignalCountReached() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ val model by collectLastValue(underTest.educationTriggered)
+
+ assertThat(model?.gestureType).isEqualTo(gestureType)
+ }
+
+ @Test
+ fun newEducationToastOn1stEducation() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast)
+ }
+
+ @Test
+ fun newEducationNotificationOn2ndEducation() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+ // runCurrent() to trigger 1st education
+ runCurrent()
+
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
+ }
+
+ @Test
+ fun noEducationInfoBeforeMaxSignalCountReached() =
+ testScope.runTest {
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ val model by collectLastValue(underTest.educationTriggered)
+ assertThat(model).isNull()
+ }
+
+ @Test
+ fun noEducationInfoWhenShortcutTriggeredPreviously() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ contextualEduInteractor.updateShortcutTriggerTime(gestureType)
+ triggerMaxEducationSignals(gestureType)
+ assertThat(model).isNull()
+ }
+
+ @Test
+ fun no2ndEducationBeforeMinEduIntervalReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ // Offset a duration that is less than the required education interval
+ eduClock.offset(1.seconds)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ assertThat(models.filterNotNull().size).isEqualTo(1)
+ }
+
+ @Test
+ fun noNewEducationInfoAfterMaxEducationCountReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ // Trigger 2 educations
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ // Try triggering 3rd education
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(models.filterNotNull().size).isEqualTo(2)
+ }
+
+ @Test
+ fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
+ testScope.runTest {
+ val model by
+ collectLastValue(
+ kosmos.contextualEducationRepository.readGestureEduModelFlow(gestureType)
+ )
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds))
+ val secondSignalReceivedTime = eduClock.instant()
+ contextualEduInteractor.incrementSignalCount(gestureType)
+
+ assertThat(model)
+ .isEqualTo(
+ GestureEduModel(
+ signalCount = 1,
+ usageSessionStartTime = secondSignalReceivedTime,
+ userId = 0,
+ gestureType = gestureType,
+ )
+ )
+ }
+
+ @Test
+ fun newTouchpadConnectionTimeOnFirstTouchpadConnected() =
+ testScope.runTest {
+ setIsAnyTouchpadConnected(true)
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun unchangedTouchpadConnectionTimeOnSecondConnection() =
+ testScope.runTest {
+ val firstConnectionTime = eduClock.instant()
+ setIsAnyTouchpadConnected(true)
+ setIsAnyTouchpadConnected(false)
+
+ eduClock.offset(1.hours)
+ setIsAnyTouchpadConnected(true)
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(firstConnectionTime)
+ }
+
+ @Test
+ fun newTouchpadConnectionTimeOnUserChanged() =
+ testScope.runTest {
+ // Touchpad connected for user 0
+ setIsAnyTouchpadConnected(true)
+
+ // Change user
+ eduClock.offset(1.hours)
+ val newUserFirstConnectionTime = eduClock.instant()
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ runCurrent()
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+ }
+
+ @Test
+ fun newKeyboardConnectionTimeOnKeyboardConnected() =
+ testScope.runTest {
+ setIsAnyKeyboardConnected(true)
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun unchangedKeyboardConnectionTimeOnSecondConnection() =
+ testScope.runTest {
+ val firstConnectionTime = eduClock.instant()
+ setIsAnyKeyboardConnected(true)
+ setIsAnyKeyboardConnected(false)
+
+ eduClock.offset(1.hours)
+ setIsAnyKeyboardConnected(true)
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(firstConnectionTime)
+ }
+
+ @Test
+ fun newKeyboardConnectionTimeOnUserChanged() =
+ testScope.runTest {
+ // Keyboard connected for user 0
+ setIsAnyKeyboardConnected(true)
+
+ // Change user
+ eduClock.offset(1.hours)
+ val newUserFirstConnectionTime = eduClock.instant()
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ runCurrent()
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+ }
+
+ @Test
+ fun updateShortcutTimeOnKeyboardShortcutTriggered() =
+ testScope.runTest {
+ // Only All Apps needs to update the keyboard shortcut
+ assumeTrue(gestureType == ALL_APPS)
+ kosmos.contextualEducationRepository.setKeyboardShortcutTriggered(ALL_APPS)
+
+ val model by
+ collectLastValue(
+ kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
+ )
+ assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
+ testScope.runTest {
+ assumeTrue(gestureType != ALL_APPS)
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
+ testScope.runTest {
+ assumeTrue(gestureType == ALL_APPS)
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataAddedOnUpdateShortcutTriggerTime() =
+ testScope.runTest {
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ assertThat(model?.lastShortcutTriggeredTime).isNull()
+
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ true, gestureType)
+
+ assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ eduClock.offset(initialDelayElapsedDuration)
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ // No offset to the clock to simulate update before initial delay
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+ testScope.runTest {
+ // No update to OOBE launch time to simulate no OOBE is launched yet
+ setUpForDeviceConnection()
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ private suspend fun setUpForInitialDelayElapse() {
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, eduClock.instant())
+ eduClock.offset(initialDelayElapsedDuration)
+ }
+
+ fun logMetricsForToastEducation() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ verify(kosmos.mockEduMetricsLogger)
+ .logContextualEducationTriggered(gestureType, EducationUiType.Toast)
+ }
+
+ @Test
+ fun logMetricsForNotificationEducation() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ verify(kosmos.mockEduMetricsLogger)
+ .logContextualEducationTriggered(gestureType, EducationUiType.Notification)
+ }
+
+ @After
+ fun clear() {
+ testScope.launch { tutorialSchedulerRepository.clear() }
+ }
+
+ private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
+ // Increment max number of signal to try triggering education
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ }
+ }
+
+ private fun TestScope.setIsAnyTouchpadConnected(isConnected: Boolean) {
+ touchpadRepository.setIsAnyTouchpadConnected(isConnected)
+ runCurrent()
+ }
+
+ private fun TestScope.setIsAnyKeyboardConnected(isConnected: Boolean) {
+ keyboardRepository.setIsAnyKeyboardConnected(isConnected)
+ runCurrent()
+ }
+
+ private fun setUpForDeviceConnection() {
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ }
+
+ private fun getOverviewProxyListener(): OverviewProxyListener {
+ val listenerCaptor = argumentCaptor<OverviewProxyListener>()
+ verify(overviewProxyService).addCallback(listenerCaptor.capture())
+ return listenerCaptor.firstValue
+ }
+
+ companion object {
+ private val USER_INFOS = listOf(UserInfo(101, "Second User", 0))
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getGestureTypes(): List<GestureType> {
+ return listOf(BACK, HOME, OVERVIEW, ALL_APPS)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 2a6d29c61890..580f631734e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 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,19 +16,17 @@
package com.android.systemui.education.domain.interactor
-import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
-import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
import com.android.systemui.contextualeducation.GestureType.HOME
import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.education.data.model.GestureEduModel
-import com.android.systemui.education.data.repository.contextualEducationRepository
import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.education.shared.model.EducationInfo
import com.android.systemui.education.shared.model.EducationUiType
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
@@ -37,50 +35,42 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.verify
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
+@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
-class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) : SysuiTestCase() {
+class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val contextualEduInteractor = kosmos.contextualEducationInteractor
- private val repository = kosmos.contextualEducationRepository
private val touchpadRepository = kosmos.touchpadRepository
private val keyboardRepository = kosmos.keyboardRepository
private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
- private val userRepository = kosmos.fakeUserRepository
private val overviewProxyService = kosmos.mockOverviewProxyService
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
private val eduClock = kosmos.fakeEduClock
- private val minDurationForNextEdu =
- KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
private val initialDelayElapsedDuration =
KeyboardTouchpadEduInteractor.initialDelayDuration + 1.seconds
+ private val minIntervalForEduNotification =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
@Before
fun setup() {
underTest.start()
contextualEduInteractor.start()
- userRepository.setUserInfos(USER_INFOS)
testScope.launch {
contextualEduInteractor.updateKeyboardFirstConnectionTime()
contextualEduInteractor.updateTouchpadFirstConnectionTime()
@@ -88,312 +78,76 @@ class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) :
}
@Test
- fun newEducationInfoOnMaxSignalCountReached() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- val model by collectLastValue(underTest.educationTriggered)
-
- assertThat(model?.gestureType).isEqualTo(gestureType)
- }
-
- @Test
- fun newEducationToastOn1stEducation() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast)
- }
-
- @Test
- fun newEducationNotificationOn2ndEducation() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
- // runCurrent() to trigger 1st education
- runCurrent()
-
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
- }
-
- @Test
- fun noEducationInfoBeforeMaxSignalCountReached() =
- testScope.runTest {
- contextualEduInteractor.incrementSignalCount(gestureType)
- val model by collectLastValue(underTest.educationTriggered)
- assertThat(model).isNull()
- }
-
- @Test
- fun noEducationInfoWhenShortcutTriggeredPreviously() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- contextualEduInteractor.updateShortcutTriggerTime(gestureType)
- triggerMaxEducationSignals(gestureType)
- assertThat(model).isNull()
- }
-
- @Test
- fun no2ndEducationBeforeMinEduIntervalReached() =
- testScope.runTest {
- val models by collectValues(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- // Offset a duration that is less than the required education interval
- eduClock.offset(1.seconds)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- assertThat(models.filterNotNull().size).isEqualTo(1)
- }
-
- @Test
- fun noNewEducationInfoAfterMaxEducationCountReached() =
- testScope.runTest {
- val models by collectValues(underTest.educationTriggered)
- // Trigger 2 educations
- triggerMaxEducationSignals(gestureType)
- runCurrent()
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- // Try triggering 3rd education
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(models.filterNotNull().size).isEqualTo(2)
- }
-
- @Test
- fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
- testScope.runTest {
- val model by
- collectLastValue(
- kosmos.contextualEducationRepository.readGestureEduModelFlow(gestureType)
- )
- contextualEduInteractor.incrementSignalCount(gestureType)
- eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds))
- val secondSignalReceivedTime = eduClock.instant()
- contextualEduInteractor.incrementSignalCount(gestureType)
-
- assertThat(model)
- .isEqualTo(
- GestureEduModel(
- signalCount = 1,
- usageSessionStartTime = secondSignalReceivedTime,
- userId = 0,
- gestureType = gestureType,
- )
- )
- }
-
- @Test
- fun newTouchpadConnectionTimeOnFirstTouchpadConnected() =
- testScope.runTest {
- setIsAnyTouchpadConnected(true)
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun unchangedTouchpadConnectionTimeOnSecondConnection() =
- testScope.runTest {
- val firstConnectionTime = eduClock.instant()
- setIsAnyTouchpadConnected(true)
- setIsAnyTouchpadConnected(false)
-
- eduClock.offset(1.hours)
- setIsAnyTouchpadConnected(true)
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(firstConnectionTime)
- }
-
- @Test
- fun newTouchpadConnectionTimeOnUserChanged() =
- testScope.runTest {
- // Touchpad connected for user 0
- setIsAnyTouchpadConnected(true)
-
- // Change user
- eduClock.offset(1.hours)
- val newUserFirstConnectionTime = eduClock.instant()
- userRepository.setSelectedUserInfo(USER_INFOS[0])
- runCurrent()
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
- }
-
- @Test
- fun newKeyboardConnectionTimeOnKeyboardConnected() =
- testScope.runTest {
- setIsAnyKeyboardConnected(true)
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun unchangedKeyboardConnectionTimeOnSecondConnection() =
- testScope.runTest {
- val firstConnectionTime = eduClock.instant()
- setIsAnyKeyboardConnected(true)
- setIsAnyKeyboardConnected(false)
-
- eduClock.offset(1.hours)
- setIsAnyKeyboardConnected(true)
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(firstConnectionTime)
- }
-
- @Test
- fun newKeyboardConnectionTimeOnUserChanged() =
- testScope.runTest {
- // Keyboard connected for user 0
- setIsAnyKeyboardConnected(true)
-
- // Change user
- eduClock.offset(1.hours)
- val newUserFirstConnectionTime = eduClock.instant()
- userRepository.setSelectedUserInfo(USER_INFOS[0])
- runCurrent()
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
- }
-
- @Test
- fun updateShortcutTimeOnKeyboardShortcutTriggered() =
- testScope.runTest {
- // Only All Apps needs to update the keyboard shortcut
- assumeTrue(gestureType == ALL_APPS)
- kosmos.contextualEducationRepository.setKeyboardShortcutTriggered(ALL_APPS)
-
- val model by
- collectLastValue(
- kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
- )
- assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
- testScope.runTest {
- assumeTrue(gestureType != ALL_APPS)
- setUpForInitialDelayElapse()
- touchpadRepository.setIsAnyTouchpadConnected(true)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
- }
-
- @Test
- fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
- testScope.runTest {
- setUpForInitialDelayElapse()
- touchpadRepository.setIsAnyTouchpadConnected(false)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue)
- }
-
- @Test
- fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
- testScope.runTest {
- assumeTrue(gestureType == ALL_APPS)
- setUpForInitialDelayElapse()
- keyboardRepository.setIsAnyKeyboardConnected(true)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
- }
-
- @Test
- fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+ fun newEducationToastBeforeMaxToastsPerSessionTriggered() =
testScope.runTest {
+ setUpForDeviceConnection()
setUpForInitialDelayElapse()
- keyboardRepository.setIsAnyKeyboardConnected(false)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue)
- }
-
- @Test
- fun dataAddedOnUpdateShortcutTriggerTime() =
- testScope.runTest {
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- assertThat(model?.lastShortcutTriggeredTime).isNull()
+ val model by collectLastValue(underTest.educationTriggered)
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ true, gestureType)
+ triggerEducation(HOME)
- assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
+ assertThat(model).isEqualTo(EducationInfo(HOME, EducationUiType.Toast, userId = 0))
}
@Test
- fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+ fun noEducationToastAfterMaxToastsPerSessionTriggered() =
testScope.runTest {
setUpForDeviceConnection()
- tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ // Show two toasts of other gestures
+ triggerEducation(HOME)
+ triggerEducation(BACK)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- eduClock.offset(initialDelayElapsedDuration)
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ triggerEducation(OVERVIEW)
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ // No new toast education besides the 2 triggered at first
+ val firstEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu).inOrder()
}
@Test
- fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+ fun newEducationToastAfterMinIntervalElapsedWhenMaxToastsPerSessionTriggered() =
testScope.runTest {
setUpForDeviceConnection()
- tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ // Show two toasts of other gestures
+ triggerEducation(HOME)
+ triggerEducation(BACK)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- // No offset to the clock to simulate update before initial delay
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ // Trigger toast after an usage session has elapsed
+ eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration + 1.seconds)
+ triggerEducation(OVERVIEW)
- assertThat(model?.signalCount).isEqualTo(originalValue)
+ val firstEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ val thirdEdu = EducationInfo(OVERVIEW, EducationUiType.Toast, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu, thirdEdu).inOrder()
}
@Test
- fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+ fun newEducationNotificationAfterMaxToastsPerSessionTriggered() =
testScope.runTest {
- // No update to OOBE launch time to simulate no OOBE is launched yet
setUpForDeviceConnection()
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ triggerEducation(BACK)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ // Offset to let min interval for notification elapse so we could show edu notification
+ // for BACK. It would be a new usage session too because the interval (7 days) is
+ // longer than a usage session (3 days)
+ eduClock.offset(minIntervalForEduNotification)
+ triggerEducation(HOME)
+ triggerEducation(OVERVIEW)
+ triggerEducation(BACK)
- assertThat(model?.signalCount).isEqualTo(originalValue)
+ val firstEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val thirdEdu = EducationInfo(OVERVIEW, EducationUiType.Toast, userId = 0)
+ val fourthEdu = EducationInfo(BACK, EducationUiType.Notification, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu, thirdEdu, fourthEdu).inOrder()
}
private suspend fun setUpForInitialDelayElapse() {
@@ -402,51 +156,6 @@ class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) :
eduClock.offset(initialDelayElapsedDuration)
}
- fun logMetricsForToastEducation() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- verify(kosmos.mockEduMetricsLogger)
- .logContextualEducationTriggered(gestureType, EducationUiType.Toast)
- }
-
- @Test
- fun logMetricsForNotificationEducation() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- verify(kosmos.mockEduMetricsLogger)
- .logContextualEducationTriggered(gestureType, EducationUiType.Notification)
- }
-
- @After
- fun clear() {
- testScope.launch { tutorialSchedulerRepository.clear() }
- }
-
- private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
- // Increment max number of signal to try triggering education
- for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
- contextualEduInteractor.incrementSignalCount(gestureType)
- }
- }
-
- private fun TestScope.setIsAnyTouchpadConnected(isConnected: Boolean) {
- touchpadRepository.setIsAnyTouchpadConnected(isConnected)
- runCurrent()
- }
-
- private fun TestScope.setIsAnyKeyboardConnected(isConnected: Boolean) {
- keyboardRepository.setIsAnyKeyboardConnected(isConnected)
- runCurrent()
- }
-
private fun setUpForDeviceConnection() {
touchpadRepository.setIsAnyTouchpadConnected(true)
keyboardRepository.setIsAnyKeyboardConnected(true)
@@ -458,13 +167,12 @@ class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) :
return listenerCaptor.firstValue
}
- companion object {
- private val USER_INFOS = listOf(UserInfo(101, "Second User", 0))
-
- @JvmStatic
- @Parameters(name = "{0}")
- fun getGestureTypes(): List<GestureType> {
- return listOf(BACK, HOME, OVERVIEW, ALL_APPS)
+ private fun TestScope.triggerEducation(gestureType: GestureType) {
+ // Increment max number of signal to try triggering education
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
}
+ runCurrent()
}
}
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 194f4560bb19..38acd23d282c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -137,6 +137,7 @@ import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
+import com.android.window.flags.Flags;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -157,6 +158,10 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
@SmallTest
public class KeyguardViewMediatorTest extends SysuiTestCase {
+
+ private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
+ Flags.ensureKeyguardDoesTransitionStarting();
+
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private KeyguardViewMediator mViewMediator;
@@ -1164,6 +1169,29 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
*/
private void assertATMSLockScreenShowing(boolean showing)
throws RemoteException {
+
+ if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ // ATMS is called via bgExecutor, so make sure to run all of those calls first.
+ processAllMessagesAndBgExecutorMessages();
+
+ final InOrder orderedSetLockScreenShownCalls = inOrder(mKeyguardTransitions);
+ final ArgumentCaptor<Boolean> showingCaptor = ArgumentCaptor.forClass(Boolean.class);
+ orderedSetLockScreenShownCalls
+ .verify(mKeyguardTransitions, atLeastOnce())
+ .startKeyguardTransition(showingCaptor.capture(), anyBoolean());
+
+ // The captor will have the most recent startKeyguardTransition call's value.
+ assertEquals(showing, showingCaptor.getValue());
+
+ // We're now just after the last startKeyguardTransition call. If we expect the
+ // lockscreen to be showing, ensure that we didn't subsequently ask for it to go away.
+ if (showing) {
+ orderedSetLockScreenShownCalls.verify(mKeyguardTransitions, never())
+ .startKeyguardTransition(eq(false), anyBoolean());
+ }
+ return;
+ }
+
// ATMS is called via bgExecutor, so make sure to run all of those calls first.
processAllMessagesAndBgExecutorMessages();
@@ -1192,6 +1220,20 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// ATMS is called via bgExecutor, so make sure to run all of those calls first.
processAllMessagesAndBgExecutorMessages();
+ if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ final InOrder orderedGoingAwayCalls = inOrder(mKeyguardTransitions);
+ orderedGoingAwayCalls.verify(mKeyguardTransitions, atLeastOnce())
+ .startKeyguardTransition(eq(false) /* keyguardShowing */,
+ eq(false) /* aodShowing */);
+
+ // Advance the inOrder to just past the last goingAway call. Let's make sure we didn't
+ // re-show the lockscreen, which would cancel going away.
+ orderedGoingAwayCalls.verify(mKeyguardTransitions, never())
+ .startKeyguardTransition(eq(true) /* keyguardShowing */,
+ anyBoolean() /* aodShowing */);
+ return;
+ }
+
final InOrder orderedGoingAwayCalls = inOrder(mActivityTaskManagerService);
orderedGoingAwayCalls.verify(mActivityTaskManagerService, atLeastOnce())
.keyguardGoingAway(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index d3409c7256fd..e3aeaa85873d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -484,14 +484,12 @@ class RepeatWhenAttachedTest : SysuiTestCase() {
private fun CoroutineScope.repeatWhenAttached(): DisposableHandle {
return view.repeatWhenAttached(
coroutineContext = coroutineContext,
- block = block,
+ block = { block.invoke(this) },
)
}
- private class Block : suspend LifecycleOwner.(View) -> Unit {
- data class Invocation(
- val lifecycleOwner: LifecycleOwner,
- ) {
+ private class Block {
+ data class Invocation(val lifecycleOwner: LifecycleOwner) {
val lifecycleState: Lifecycle.State
get() = lifecycleOwner.lifecycle.currentState
}
@@ -504,7 +502,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() {
val latestLifecycleState: Lifecycle.State
get() = _invocations.last().lifecycleState
- override suspend fun invoke(lifecycleOwner: LifecycleOwner, view: View) {
+ fun invoke(lifecycleOwner: LifecycleOwner) {
_invocations.add(Invocation(lifecycleOwner))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 68a5d9361046..9543032ef5ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -247,7 +247,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
mContext,
0,
intent.setPackage(mContext.packageName),
- PendingIntent.FLAG_MUTABLE
+ PendingIntent.FLAG_MUTABLE,
)
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -294,7 +294,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
override fun loadAnimator(
animId: Int,
otionInterpolator: Interpolator,
- vararg targets: View
+ vararg targets: View,
): AnimatorSet {
return mockAnimator
}
@@ -323,7 +323,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
packageName = PACKAGE,
instanceId = instanceId,
recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction),
- cardAction = smartspaceAction
+ cardAction = smartspaceAction,
)
}
@@ -370,7 +370,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
packageName = PACKAGE,
token = session.sessionToken,
device = device,
- instanceId = instanceId
+ instanceId = instanceId,
)
}
@@ -416,7 +416,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
action1.id,
action2.id,
action3.id,
- action4.id
+ action4.id,
)
}
@@ -536,7 +536,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
playOrPause = MediaAction(icon, Runnable {}, "play", bg),
nextOrCustom = MediaAction(icon, Runnable {}, "next", bg),
custom0 = MediaAction(icon, null, "custom 0", bg),
- custom1 = MediaAction(icon, null, "custom 1", bg)
+ custom1 = MediaAction(icon, null, "custom 1", bg),
)
val state = mediaData.copy(semanticActions = semanticActions)
player.attachPlayer(viewHolder)
@@ -590,7 +590,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
custom0 = MediaAction(icon, null, "custom 0", bg),
custom1 = MediaAction(icon, null, "custom 1", bg),
false,
- true
+ true,
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -622,7 +622,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
custom0 = MediaAction(icon, null, "custom 0", bg),
custom1 = MediaAction(icon, null, "custom 1", bg),
true,
- false
+ false,
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -760,7 +760,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
val semanticActions =
MediaButton(
playOrPause = MediaAction(icon, Runnable {}, "play", null),
- nextOrCustom = MediaAction(icon, Runnable {}, "next", null)
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", null),
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -850,7 +850,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
val semanticActions =
MediaButton(
prevOrCustom = MediaAction(icon, {}, "prev", null),
- nextOrCustom = MediaAction(icon, {}, "next", null)
+ nextOrCustom = MediaAction(icon, {}, "next", null),
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -921,7 +921,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
val semanticActions =
MediaButton(
prevOrCustom = MediaAction(icon, {}, "prev", null),
- nextOrCustom = MediaAction(icon, {}, "next", null)
+ nextOrCustom = MediaAction(icon, {}, "next", null),
)
val state = mediaData.copy(semanticActions = semanticActions)
player.attachPlayer(viewHolder)
@@ -944,7 +944,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
val semanticActions =
MediaButton(
prevOrCustom = MediaAction(icon, {}, "prev", null),
- nextOrCustom = MediaAction(icon, {}, "next", null)
+ nextOrCustom = MediaAction(icon, {}, "next", null),
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -966,6 +966,29 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
+ fun setIsScrubbing_reservedButtonSpaces_scrubbingTimesShown() {
+ val semanticActions =
+ MediaButton(
+ prevOrCustom = null,
+ nextOrCustom = null,
+ reserveNext = true,
+ reservePrev = true,
+ )
+ val state = mediaData.copy(semanticActions = semanticActions)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(state, PACKAGE)
+ reset(expandedSet)
+
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ verify(expandedSet).setVisibility(R.id.actionPrev, View.GONE)
+ verify(expandedSet).setVisibility(R.id.actionNext, View.GONE)
+ verify(expandedSet).setVisibility(R.id.media_scrubbing_elapsed_time, View.VISIBLE)
+ verify(expandedSet).setVisibility(R.id.media_scrubbing_total_time, View.VISIBLE)
+ }
+
+ @Test
fun bind_resumeState_withProgress() {
val progress = 0.5
val state = mediaData.copy(resumption = true, resumeProgress = progress)
@@ -1009,13 +1032,13 @@ public class MediaControlPanelTest : SysuiTestCase() {
MediaNotificationAction(true, actionIntent = pendingIntent, icon, "play"),
MediaNotificationAction(true, actionIntent = null, icon, "next"),
MediaNotificationAction(true, actionIntent = null, icon, "custom 0"),
- MediaNotificationAction(true, actionIntent = pendingIntent, icon, "custom 1")
+ MediaNotificationAction(true, actionIntent = pendingIntent, icon, "custom 1"),
)
val state =
mediaData.copy(
actions = actions,
actionsToShowInCompact = listOf(1, 2),
- semanticActions = null
+ semanticActions = null,
)
player.attachPlayer(viewHolder)
@@ -1701,7 +1724,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
- MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
)
val data = mediaData.copy(actions = actions)
@@ -1720,7 +1743,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
- MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
)
val data = mediaData.copy(actions = actions)
@@ -1739,7 +1762,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
- MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
)
val data = mediaData.copy(actions = actions)
@@ -2021,7 +2044,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle(subtitle3)
.setIcon(icon)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
player.bindRecommendation(data)
@@ -2047,7 +2070,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setIcon(
Icon.createWithResource(
context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata
+ com.android.settingslib.R.drawable.ic_1x_mobiledata,
)
)
.setExtras(Bundle.EMPTY)
@@ -2084,7 +2107,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("fake subtitle")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
player.bindRecommendation(data)
@@ -2119,7 +2142,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
player.bindRecommendation(data)
@@ -2142,7 +2165,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setIcon(
Icon.createWithResource(
context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata
+ com.android.settingslib.R.drawable.ic_1x_mobiledata,
)
)
.setExtras(Bundle.EMPTY)
@@ -2157,11 +2180,11 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setIcon(
Icon.createWithResource(
context,
- com.android.settingslib.R.drawable.ic_3g_mobiledata
+ com.android.settingslib.R.drawable.ic_3g_mobiledata,
)
)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2185,7 +2208,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setIcon(
Icon.createWithResource(
context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata
+ com.android.settingslib.R.drawable.ic_1x_mobiledata,
)
)
.setExtras(Bundle.EMPTY)
@@ -2200,11 +2223,11 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setIcon(
Icon.createWithResource(
context,
- com.android.settingslib.R.drawable.ic_3g_mobiledata
+ com.android.settingslib.R.drawable.ic_3g_mobiledata,
)
)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2245,7 +2268,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("subtitle1")
.setIcon(albumArt)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2268,7 +2291,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
Bundle().apply {
putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
- MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
+ MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED,
)
putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.5)
}
@@ -2290,7 +2313,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("subtitle1")
.setIcon(albumArt)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2328,7 +2351,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("subtitle1")
.setIcon(albumArt)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2381,7 +2404,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
.setSubtitle("subtitle1")
.setIcon(albumArt)
.setExtras(Bundle.EMPTY)
- .build()
+ .build(),
)
)
@@ -2444,7 +2467,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
icon = null,
action = {},
contentDescription = "play",
- background = null
+ background = null,
)
)
val data = mediaData.copy(semanticActions = semanticActions)
@@ -2465,7 +2488,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
icon = null,
action = {},
contentDescription = "play",
- background = null
+ background = null,
)
)
val data = mediaData.copy(semanticActions = semanticActions)
@@ -2498,7 +2521,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
icon = null,
action = {},
contentDescription = "play",
- background = null
+ background = null,
)
)
val data = mediaData.copy(semanticActions = semanticActions)
@@ -2530,8 +2553,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
icon = null,
action = {},
contentDescription = "custom0",
- background = null
- ),
+ background = null,
+ )
)
val data = mediaData.copy(semanticActions = semanticActions)
player.attachPlayer(viewHolder)
@@ -2553,8 +2576,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
icon = null,
action = {},
contentDescription = "custom0",
- background = null
- ),
+ background = null,
+ )
)
val data = mediaData.copy(semanticActions = semanticActions)
player.attachPlayer(viewHolder)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index 4da7b2ac3700..e035a02ecf00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -64,9 +64,11 @@ public class SeekBarViewModelTest : SysuiTestCase() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
+
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
+
override fun isMainThread(): Boolean {
return true
}
@@ -805,4 +807,32 @@ public class SeekBarViewModelTest : SysuiTestCase() {
fakeExecutor.runAllReady()
verify(mockController).unregisterCallback(any())
}
+
+ @Test
+ fun positionUpdatedWhileStopped() {
+ // When playback is stopped at one position
+ val firstPosition = 200L
+ val state =
+ PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_STOPPED, firstPosition, 1f)
+ build()
+ }
+ whenever(mockController.playbackState).thenReturn(state)
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ viewModel.updateController(mockController)
+ verify(mockController).registerCallback(captor.capture())
+ assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(firstPosition.toInt())
+
+ // And the state is updated with a new position
+ val secondPosition = 42L
+ val secondState =
+ PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_STOPPED, secondPosition, 1f)
+ build()
+ }
+ captor.value.onPlaybackStateChanged(secondState)
+
+ // THEN then elapsed time should be updated
+ assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(secondPosition.toInt())
+ }
}
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 d88d69da5e59..fc720b836f72 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
@@ -22,7 +22,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.filter
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
@@ -84,7 +87,7 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0])
+ listState.onStarted(TestEditTiles[0], DragType.Add)
// Tile is being dragged, it should be replaced with a placeholder
composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist()
@@ -110,8 +113,8 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0])
- listState.onMoved(1, false)
+ listState.onStarted(TestEditTiles[0], DragType.Add)
+ listState.onTargeting(1, false)
listState.onDrop()
// Available tiles should re-appear
@@ -137,7 +140,7 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0])
+ listState.onStarted(TestEditTiles[0], DragType.Add)
listState.movedOutOfBounds()
listState.onDrop()
@@ -162,11 +165,11 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(createEditTile("newTile"))
+ listState.onStarted(createEditTile("newTile"), DragType.Add)
// Insert after tileD, which is at index 4
// [ a ] [ b ] [ c ] [ empty ]
// [ tile d ] [ e ]
- listState.onMoved(4, insertAfter = true)
+ listState.onTargeting(4, insertAfter = true)
listState.onDrop()
// Available tiles should re-appear
@@ -182,11 +185,14 @@ class DragAndDropTest : SysuiTestCase() {
}
private fun ComposeContentTestRule.assertTileGridContainsExactly(specs: List<String>) {
- onNodeWithTag(CURRENT_TILES_GRID_TEST_TAG).onChildren().apply {
- fetchSemanticsNodes().forEachIndexed { index, _ ->
- get(index).assert(hasContentDescription(specs[index]))
+ onNodeWithTag(CURRENT_TILES_GRID_TEST_TAG)
+ .onChildren()
+ .filter(SemanticsMatcher.keyIsDefined(SemanticsProperties.ContentDescription))
+ .apply {
+ fetchSemanticsNodes().forEachIndexed { index, _ ->
+ get(index).assert(hasContentDescription(specs[index]))
+ }
}
- }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 90ffaf19be96..67c5986fe5d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -236,7 +236,7 @@ class QSTileViewImplTest : SysuiTestCase() {
context.orCreateTestableResources.addOverride(
R.array.tile_states_internet,
- arrayOf(unavailableString, offString, onString)
+ arrayOf(unavailableString, offString, onString),
)
// State UNAVAILABLE
@@ -341,7 +341,7 @@ class QSTileViewImplTest : SysuiTestCase() {
val testA11yLabel = "TEST_LABEL"
context.orCreateTestableResources.addOverride(
R.string.accessibility_tile_disabled_by_policy_action_description,
- testA11yLabel
+ testA11yLabel,
)
val stateDisabledByPolicy = QSTile.State()
@@ -374,7 +374,7 @@ class QSTileViewImplTest : SysuiTestCase() {
context.orCreateTestableResources.addOverride(
R.array.tile_states_internet,
- arrayOf(unavailableString, offString, onString)
+ arrayOf(unavailableString, offString, onString),
)
tileView.changeState(state)
@@ -477,6 +477,24 @@ class QSTileViewImplTest : SysuiTestCase() {
}
@Test
+ fun onStateChange_fromLongPress_toNoLongPress_whileLongPressRuns_doesNotClearResources() {
+ // GIVEN that the long-press effect has been initialized
+ val state = QSTile.State()
+ state.handlesLongClick = true
+ tileView.changeState(state)
+
+ // WHEN the long-press effect is running
+ kosmos.qsLongPressEffect.setState(QSLongPressEffect.State.RUNNING_FORWARD)
+
+ // WHEN a state changed happens so that the tile no longer handles long-press
+ state.handlesLongClick = false
+ tileView.changeState(state)
+
+ // THEN the long-press effect resources are not cleared
+ assertThat(tileView.areLongPressEffectPropertiesSet).isTrue()
+ }
+
+ @Test
fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() {
// GIVEN a tile where the long-press effect is null
tileView = FakeTileView(context, false, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 9a924ed5a630..d8d6f2e9fbb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -4,9 +4,10 @@ import android.bluetooth.BluetoothDevice
import android.os.Handler
import android.os.Looper
import android.os.UserManager
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.telephony.flags.Flags
@@ -22,8 +23,11 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
@@ -41,11 +45,17 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-class BluetoothTileTest : SysuiTestCase() {
+class BluetoothTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var qsHost: QSHost
@@ -81,7 +91,7 @@ class BluetoothTileTest : SysuiTestCase() {
qsLogger,
bluetoothController,
featureFlags,
- bluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel,
)
tile.initialize()
@@ -109,8 +119,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -121,8 +130,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -133,8 +141,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_on))
}
@Test
@@ -145,8 +152,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_search))
}
@Test
@@ -161,11 +167,10 @@ class BluetoothTileTest : SysuiTestCase() {
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
- Utils.formatPercentage(50)
+ Utils.formatPercentage(50),
)
)
- verify(bluetoothController)
- .addOnMetadataChangedListener(eq(cachedDevice), any(), any())
+ verify(bluetoothController).addOnMetadataChangedListener(eq(cachedDevice), any(), any())
}
@Test
@@ -186,7 +191,7 @@ class BluetoothTileTest : SysuiTestCase() {
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
- Utils.formatPercentage(25)
+ Utils.formatPercentage(25),
)
)
verify(bluetoothController, times(1))
@@ -197,7 +202,7 @@ class BluetoothTileTest : SysuiTestCase() {
fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(false)
+ .thenReturn(false)
`when`(clickJob.isCompleted).thenReturn(false)
tile.mClickJob = clickJob
@@ -210,7 +215,7 @@ class BluetoothTileTest : SysuiTestCase() {
fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(false)
+ .thenReturn(false)
tile.handleClick(null)
@@ -221,7 +226,7 @@ class BluetoothTileTest : SysuiTestCase() {
fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(true)
+ .thenReturn(true)
tile.handleClick(null)
@@ -265,7 +270,7 @@ class BluetoothTileTest : SysuiTestCase() {
qsLogger: QSLogger,
bluetoothController: BluetoothController,
featureFlags: FeatureFlagsClassic,
- bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel: BluetoothTileDialogViewModel,
) :
BluetoothTile(
qsHost,
@@ -279,13 +284,13 @@ class BluetoothTileTest : SysuiTestCase() {
qsLogger,
bluetoothController,
featureFlags,
- bluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel,
) {
var restrictionChecked: String? = null
override fun checkIfRestrictionEnforcedByAdminOnly(
state: QSTile.State?,
- userRestriction: String?
+ userRestriction: String?,
) {
restrictionChecked = userRestriction
}
@@ -321,7 +326,7 @@ class BluetoothTileTest : SysuiTestCase() {
fun listenToDeviceMetadata(
state: QSTile.BooleanState,
cachedDevice: CachedBluetoothDevice,
- batteryLevel: Int
+ batteryLevel: Int,
) {
val btDevice = mock<BluetoothDevice>()
whenever(cachedDevice.device).thenReturn(btDevice)
@@ -332,4 +337,20 @@ class BluetoothTileTest : SysuiTestCase() {
addConnectedDevice(cachedDevice)
tile.handleUpdateState(state, /* arg= */ null)
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 6a43a61dad77..55fb6dacfc3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -21,15 +21,15 @@ import android.content.ContextWrapper
import android.content.SharedPreferences
import android.os.Handler
import android.platform.test.annotations.DisableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.TestableLooper
import android.view.ContextThemeWrapper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -39,14 +39,19 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -55,56 +60,55 @@ import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.io.File
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@DisableFlags(android.app.Flags.FLAG_MODES_UI)
-class DndTileTest : SysuiTestCase() {
+class DndTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
private const val DEFAULT_USER = 0
private const val KEY = Settings.Secure.ZEN_DURATION
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
}
- @Mock
- private lateinit var qsHost: QSHost
+ @Mock private lateinit var qsHost: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var uiEventLogger: QsEventLogger
- @Mock
- private lateinit var zenModeController: ZenModeController
+ @Mock private lateinit var zenModeController: ZenModeController
- @Mock
- private lateinit var sharedPreferences: SharedPreferences
+ @Mock private lateinit var sharedPreferences: SharedPreferences
- @Mock
- private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
+ @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
- @Mock
- private lateinit var hostDialog: Dialog
+ @Mock private lateinit var hostDialog: Dialog
- @Mock
- private lateinit var expandable: Expandable
+ @Mock private lateinit var expandable: Expandable
- @Mock
- private lateinit var controller: DialogTransitionAnimator.Controller
+ @Mock private lateinit var controller: DialogTransitionAnimator.Controller
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
@@ -118,31 +122,32 @@ class DndTileTest : SysuiTestCase() {
whenever(qsHost.userId).thenReturn(DEFAULT_USER)
- val wrappedContext = object : ContextWrapper(
- ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
- ) {
- override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
- return sharedPreferences
+ val wrappedContext =
+ object :
+ ContextWrapper(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) {
+ override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
+ return sharedPreferences
+ }
}
- }
whenever(qsHost.context).thenReturn(wrappedContext)
whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
- tile = DndTile(
- qsHost,
- uiEventLogger,
- testableLooper.looper,
- Handler(testableLooper.looper),
- FalsingManagerFake(),
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger,
- zenModeController,
- sharedPreferences,
- secureSettings,
- mDialogTransitionAnimator
- )
+ tile =
+ DndTile(
+ qsHost,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ zenModeController,
+ sharedPreferences,
+ secureSettings,
+ mDialogTransitionAnimator,
+ )
}
@After
@@ -222,7 +227,7 @@ class DndTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_dnd_icon_off))
}
@Test
@@ -232,6 +237,14 @@ class DndTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_dnd_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index 190d80f9f6c4..5f63b15916a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -32,12 +36,12 @@ import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -45,9 +49,11 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -63,11 +69,21 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class DreamTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private ActivityStarter mActivityStarter;
@Mock
@@ -101,6 +117,11 @@ public class DreamTileTest extends SysuiTestCase {
private final String mExpectedTileLabel = mContext.getResources().getString(
R.string.quick_settings_screensaver_label);
+ public DreamTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -246,13 +267,13 @@ public class DreamTileTest extends SysuiTestCase {
dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_DESK);
receiver.onReceive(mContext, dockIntent);
mTestableLooper.processAllMessages();
- assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver),
+ assertEquals(createExpectedIcon(R.drawable.ic_qs_screen_saver),
dockedTile.getState().icon);
dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
receiver.onReceive(mContext, dockIntent);
mTestableLooper.processAllMessages();
- assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
+ assertEquals(createExpectedIcon(R.drawable.ic_qs_screen_saver_undocked),
dockedTile.getState().icon);
destroyTile(dockedTile);
@@ -268,6 +289,14 @@ public class DreamTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
private DreamTile constructTileForTest(boolean dreamSupported,
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index 5bd6944e863f..ba6c2dc4f705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -16,16 +16,20 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -38,6 +42,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+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.res.R;
@@ -54,11 +59,21 @@ import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HotspotTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
@@ -74,6 +89,11 @@ public class HotspotTileTest extends SysuiTestCase {
private HotspotTile mTile;
private QSTile.BooleanState mState = new QSTile.BooleanState();
+ public HotspotTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
mTestableLooper = TestableLooper.get(this);
@@ -144,7 +164,7 @@ public class HotspotTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_off));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_off));
}
@Test
@@ -156,7 +176,7 @@ public class HotspotTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_search));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_search));
}
@Test
@@ -168,6 +188,14 @@ public class HotspotTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_on));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_on));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 5ada2f3fd63d..b26f0a6e71a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -260,7 +260,8 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mConnectivityManager.getNetworkCapabilities(mNetwork))
@@ -272,7 +273,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
verify(mToastFactory).createToast(any(), any(), eq(TOAST_MESSAGE_STRING), anyString(),
@@ -281,11 +282,12 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenSettingsOff() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
-
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(false);
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
@@ -294,12 +296,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenKeyguardLocked() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
@@ -308,7 +311,8 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenMobileIsPrimary() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mConnectivityManager.getNetworkCapabilities(mNetwork))
@@ -446,10 +450,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
spyController.onAccessPointsChanged(null /* accessPoints */);
- doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
+ doReturn(SUB_ID).when(spyController).getActiveAutoSwitchNonDdsSubId();
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
- doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+ spyController.mSubIdServiceState.put(SUB_ID2, mServiceState);
assertFalse(TextUtils.equals(spyController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
@@ -469,8 +472,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
spyController.onAccessPointsChanged(null /* accessPoints */);
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
- doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+ spyController.mSubIdServiceState.put(SUB_ID, mServiceState);
assertTrue(TextUtils.equals(spyController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
@@ -487,11 +489,10 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
fakeAirplaneModeEnabled(false);
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+ InternetDialogController spyController = spy(mInternetDialogController);
doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
-
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+ spyController.mSubIdServiceState.put(SUB_ID, mServiceState);
assertThat(mInternetDialogController.getSubtitleText(false))
.isEqualTo(getResourcesString("non_carrier_network_unavailable"));
@@ -499,6 +500,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
// if the Wi-Fi disallow config, then don't return Wi-Fi related string.
mInternetDialogController.mCanConfigWifi = false;
+ when(spyController.isMobileDataEnabled()).thenReturn(false);
+
+
assertThat(mInternetDialogController.getSubtitleText(false))
.isNotEqualTo(getResourcesString("non_carrier_network_unavailable"));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 7b24233d8603..300c9b843d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -6,6 +6,7 @@ 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.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -23,8 +24,8 @@ import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
-import androidx.test.annotation.UiThreadTest;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -40,8 +41,6 @@ import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wifitrackerlib.WifiEntry;
-import kotlinx.coroutines.CoroutineScope;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -52,6 +51,8 @@ import org.mockito.MockitoSession;
import java.util.List;
+import kotlinx.coroutines.CoroutineScope;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -127,7 +128,7 @@ public class InternetDialogDelegateTest extends SysuiTestCase {
.spyStatic(WifiEnterpriseRestrictionUtils.class)
.startMocking();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
- when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class)))
+ when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class), eq(mContext)))
.thenReturn(mSystemUIDialog);
when(mSystemUIDialog.getContext()).thenReturn(mContext);
when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index e7fb470cfa76..c410111bc2e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,7 +33,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -43,7 +43,9 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor
import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -131,20 +133,26 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
underTest =
GlanceableHubContainerController(
communalInteractor,
+ communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
- kosmos.keyguardTransitionInteractor,
+ keyguardTransitionInteractor,
shadeInteractor,
powerManager,
communalColors,
ambientTouchComponentFactory,
communalContent,
- kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController,
- kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController,
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
logcatLogBuffer("GlanceableHubContainerControllerTest"),
)
+
+ // Make below last notification true by default or else touches will be ignored by
+ // default when the hub is not showing.
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
}
testableLooper = TestableLooper.get(this)
@@ -178,6 +186,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -207,6 +216,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
val underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -231,6 +241,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
val underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -631,7 +642,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_shadeInteracting_movesNotDispatched() =
with(kosmos) {
@@ -688,7 +699,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
with(kosmos) {
@@ -721,11 +732,13 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
- @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() =
with(kosmos) {
testScope.runTest {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
// On lockscreen.
goToScene(CommunalScenes.Blank)
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 63e56eeb730f..8045a13ff9be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.when;
import android.graphics.drawable.Icon;
import android.os.Handler;
-import android.platform.test.annotations.EnableFlags;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
import android.view.WindowManager;
@@ -34,7 +33,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -95,7 +93,6 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
@@ -114,7 +111,6 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
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 105cf168995c..20ecaf75c625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -31,7 +31,6 @@ import android.annotation.Nullable;
import android.app.Dialog;
import android.graphics.drawable.Icon;
import android.os.Handler;
-import android.platform.test.annotations.EnableFlags;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
import android.view.WindowManager;
@@ -39,7 +38,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -131,7 +129,6 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
KeyboardShortcuts.toggle(mContext, DEVICE_ID);
@@ -143,7 +140,6 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
KeyboardShortcuts.toggle(mContext, DEVICE_ID);
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 11b19f95c1c0..a91fb45c9c80 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
@@ -95,6 +95,7 @@ import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShade
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.AvalancheController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
@@ -103,7 +104,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.notification.headsup.AvalancheController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor;
@@ -356,6 +356,31 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@EnableSceneContainer
+ public void updateStackCutoff_updatesStackEndHeight() {
+ // GIVEN shade is fully open
+ final float stackTop = 200f;
+ final float stackCutoff = 1000f;
+ final float stackHeight = stackCutoff - stackTop;
+ mAmbientState.setStackTop(stackTop);
+ mAmbientState.setStackCutoff(stackCutoff);
+ mAmbientState.setStatusBarState(StatusBarState.SHADE);
+ mStackScroller.setMaxDisplayedNotifications(-1); // no limit on the shade
+ mStackScroller.setExpandFraction(1f); // shade is fully expanded
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(stackHeight);
+
+ // WHEN stackCutoff changes
+ final float newStackCutoff = 800;
+ mStackScroller.setStackCutoff(newStackCutoff);
+
+ // THEN stackEndHeight is updated
+ final float newStackHeight = newStackCutoff - stackTop;
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(newStackHeight);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(newStackHeight);
+ }
+
+ @Test
+ @EnableSceneContainer
public void updateStackEndHeightAndStackHeight_maxNotificationsSet_withSceneContainer() {
float stackHeight = 300f;
when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
@@ -1275,6 +1300,37 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ @EnableSceneContainer
+ public void testChildHeightUpdated_whenMaxDisplayedNotificationsSet_updatesStackHeight() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ int maxNotifs = 1; // any non-zero limit
+ float stackTop = 100;
+ float stackCutoff = 1100;
+ mStackScroller.setStackTop(stackTop);
+ mStackScroller.setStackCutoff(stackCutoff);
+
+ // Given we have a limit on max displayed notifications
+ int stackHeightBeforeUpdate = 100;
+ when(mStackSizeCalculator.computeHeight(eq(mStackScroller), eq(maxNotifs), anyFloat()))
+ .thenReturn((float) stackHeightBeforeUpdate);
+ mStackScroller.setMaxDisplayedNotifications(maxNotifs);
+
+ // And the stack heights are set
+ assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeightBeforeUpdate);
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeightBeforeUpdate);
+
+ // When a child changes its height
+ int stackHeightAfterUpdate = 300;
+ when(mStackSizeCalculator.computeHeight(eq(mStackScroller), eq(maxNotifs), anyFloat()))
+ .thenReturn((float) stackHeightAfterUpdate);
+ mStackScroller.onChildHeightChanged(row, /* needsAnimation = */ false);
+
+ // Then the stack heights are updated
+ assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeightAfterUpdate);
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeightAfterUpdate);
+ }
+
+ @Test
@DisableSceneContainer
public void testSetMaxDisplayedNotifications_notifiesListeners() {
ExpandableView.OnHeightChangedListener listener =
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 6912eda3c3d4..d157cf2d51e0 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
@@ -36,8 +36,6 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_K
import static com.google.common.truth.Truth.assertThat;
-import static kotlinx.coroutines.flow.FlowKt.flowOf;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,6 +54,8 @@ import static org.mockito.Mockito.when;
import static java.util.Collections.emptySet;
+import static kotlinx.coroutines.flow.FlowKt.flowOf;
+
import android.app.ActivityManager;
import android.app.IWallpaperManager;
import android.app.NotificationManager;
@@ -138,6 +138,7 @@ import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scene.domain.startable.ScrimStartable;
@@ -181,6 +182,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
@@ -198,7 +200,6 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -217,10 +218,6 @@ import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
-import kotlinx.coroutines.test.TestScope;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -237,6 +234,9 @@ import java.util.Set;
import javax.inject.Provider;
+import dagger.Lazy;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -667,6 +667,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
mCentralSurfaces.registerCallbacks();
+ // Clear first invocations caused by registering flows with JavaAdapter
+ mTestScope.getTestScheduler().runCurrent();
+ clearInvocations(mScrimController);
}
@Test
@@ -1159,8 +1162,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
@EnableSceneContainer
- public void brightnesShowingChanged_flagEnabled_ScrimControllerNotified() {
- mCentralSurfaces.registerCallbacks();
+ public void brightnesShowingChanged_sceneContainerFlagEnabled_ScrimControllerNotified() {
final ScrimStartable scrimStartable = mKosmos.getScrimStartable();
scrimStartable.start();
@@ -1178,9 +1180,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
@DisableSceneContainer
- public void brightnesShowingChanged_flagDisabled_ScrimControllerNotified() {
- mCentralSurfaces.registerCallbacks();
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void brightnesShowingChanged_qsUiRefactorFlagEnabled_ScrimControllerNotified() {
+ mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
+ mTestScope.getTestScheduler().runCurrent();
+ verify(mScrimController, atLeastOnce()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ clearInvocations(mScrimController);
+
+ mBrightnessMirrorShowingInteractor.setMirrorShowing(false);
+ mTestScope.getTestScheduler().runCurrent();
+ ArgumentCaptor<ScrimState> captor = ArgumentCaptor.forClass(ScrimState.class);
+ // The default is to call the one with the callback argument
+ verify(mScrimController, atLeastOnce()).legacyTransitionTo(captor.capture(), any());
+ assertThat(captor.getValue()).isNotEqualTo(ScrimState.BRIGHTNESS_MIRROR);
+ }
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void brightnesShowingChanged_flagsDisabled_ScrimControllerNotified() {
mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
mTestScope.getTestScheduler().runCurrent();
verify(mScrimController, never()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
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 abdd79761ce0..0ba0aeb87225 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
@@ -45,6 +45,7 @@ import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CommandQueue
@@ -77,6 +78,7 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.verifyNoMoreInteractions
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -86,6 +88,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
+ private val fakeShadeDisplaysRepository = kosmos.fakeShadeDisplaysRepository
@Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor
@Mock private lateinit var featureFlags: FeatureFlags
@@ -260,6 +263,64 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysDisabled_viewReceivesEvent() {
+ `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+ `when`(shadeViewController.isViewEnabled).thenReturn(true)
+ fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ view.onTouchEvent(event)
+
+ verify(shadeViewController).handleExternalTouch(event)
+ }
+
+ @Test
+ @EnableFlags(
+ AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
+ AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
+ )
+ fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_viewReceivesEvent() {
+ `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+ `when`(shadeViewController.isViewEnabled).thenReturn(true)
+ fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ view.onTouchEvent(event)
+
+ verify(shadeViewController).handleExternalTouch(event)
+ }
+
+ @Test
+ @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun handleTouchEventFromStatusBar_touchOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewReceivesEvent() {
+ `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+ `when`(shadeViewController.isViewEnabled).thenReturn(true)
+ fakeShadeDisplaysRepository.setDisplayId(DISPLAY_ID)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ view.onTouchEvent(event)
+
+ verify(shadeViewController).handleExternalTouch(event)
+ }
+
+ @Test
+ @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun handleTouchEventFromStatusBar_touchNotOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewDoesNotReceiveEvent() {
+ `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+ `when`(shadeViewController.isViewEnabled).thenReturn(true)
+ fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ view.onTouchEvent(event)
+
+ verify(shadeViewController).isViewEnabled
+ verifyNoMoreInteractions(shadeViewController)
+ }
+
+ @Test
@DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOff_viewReturnsFalse() {
`when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
@@ -432,6 +493,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
fakeDarkIconDispatcher,
statusBarContentInsetsProviderStore,
Lazy { statusBarTouchShadeDisplayPolicy },
+ fakeShadeDisplaysRepository,
)
.create(view)
.also { it.init() }
@@ -445,6 +507,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
private companion object {
- const val DISPLAY_ID = 1
+ const val DISPLAY_ID = 0
+ const val SECONDARY_DISPLAY_ID = 2
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 33a4b7ef3ed6..38ddb3e426fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -45,8 +45,6 @@ import static org.mockito.Mockito.when;
import android.animation.Animator;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -154,7 +152,6 @@ public class ScrimControllerTest extends SysuiTestCase {
private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository =
mKosmos.getKeyguardTransitionRepository();
@Mock private KeyguardInteractor mKeyguardInteractor;
- @Mock private TypedArray mMockTypedArray;
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@@ -236,12 +233,8 @@ public class ScrimControllerTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = spy(getContext());
- when(mContext.obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.materialColorSurface}))
- .thenReturn(mMockTypedArray);
-
- when(mMockTypedArray.getColorStateList(anyInt()))
- .thenAnswer((invocation) -> ColorStateList.valueOf(mSurfaceColor));
+ when(mContext.getColor(com.android.internal.R.color.materialColorSurface))
+ .thenAnswer(invocation -> mSurfaceColor);
mScrimBehind = spy(new ScrimView(mContext));
mScrimInFront = new ScrimView(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 3e24fbe6cd8d..b39e38bd71cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -533,7 +533,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+ when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -775,7 +775,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
/* hasPrimaryOngoingActivity= */ true,
/* hasSecondaryOngoingActivity= */ false,
/* shouldAnimate= */ false);
- when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+ when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -792,7 +792,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
/* hasPrimaryOngoingActivity= */ true,
/* hasSecondaryOngoingActivity= */ true,
/* shouldAnimate= */ false);
- when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+ when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -1091,9 +1091,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
- public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+ public void disable_shouldHeadsUpStatusBarBeVisibleTrue_clockDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+ when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -1102,9 +1102,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
- public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+ public void disable_shouldHeadsUpStatusBarBeVisibleFalse_clockNotDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+ when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
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 763449028f28..728f4183ccce 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
@@ -655,14 +655,14 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
// CDMA roaming is off, GSM roaming is off
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
cb.onDisplayInfoChanged(
- TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false)
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false, false, false)
)
assertThat(latest).isFalse()
// CDMA roaming is off, GSM roaming is on
cb.onDisplayInfoChanged(
- TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true)
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true, false, false)
)
assertThat(latest).isTrue()
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index cc0597bc3853..76fc61185b49 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -30,6 +30,7 @@ import android.os.Bundle
import android.os.IBinder
import android.os.UserHandle
import android.util.ArrayMap
+import android.view.Display
import android.view.KeyEvent
import com.android.internal.logging.InstanceId
import com.android.internal.statusbar.IAddTileResultCallback
@@ -95,6 +96,90 @@ class FakeStatusBarService : IStatusBarService.Stub() {
)
)
+ var statusBarIconsSecondaryDisplay =
+ ArrayMap<String, StatusBarIcon>().also {
+ it["slot1"] = mock<StatusBarIcon>()
+ it["slot2"] = mock<StatusBarIcon>()
+ }
+ var disabledFlags1SecondaryDisplay = 12345678
+ var appearanceSecondaryDisplay = 1234
+ var appearanceRegionsSecondaryDisplay =
+ arrayOf(
+ AppearanceRegion(
+ /* appearance = */ 123,
+ /* bounds = */ Rect(/* left= */ 4, /* top= */ 3, /* right= */ 2, /* bottom= */ 1),
+ ),
+ AppearanceRegion(
+ /* appearance = */ 345,
+ /* bounds = */ Rect(/* left= */ 1, /* top= */ 2, /* right= */ 3, /* bottom= */ 4),
+ ),
+ )
+ var imeWindowVisSecondaryDisplay = 9876
+ var imeBackDispositionSecondaryDisplay = 654
+ var showImeSwitcherSecondaryDisplay = true
+ var disabledFlags2SecondaryDisplay = 87654321
+ var navbarColorManagedByImeSecondaryDisplay = true
+ var behaviorSecondaryDisplay = 234
+ var requestedVisibleTypesSecondaryDisplay = 345
+ var packageNameSecondaryDisplay = "fake.bar.ser.vice"
+ var transientBarTypesSecondaryDisplay = 0
+ var letterboxDetailsSecondaryDisplay =
+ arrayOf(
+ LetterboxDetails(
+ /* letterboxInnerBounds = */ Rect(
+ /* left= */ 5,
+ /* top= */ 6,
+ /* right= */ 7,
+ /* bottom= */ 8,
+ ),
+ /* letterboxFullBounds = */ Rect(
+ /* left= */ 1,
+ /* top= */ 2,
+ /* right= */ 3,
+ /* bottom= */ 4,
+ ),
+ /* appAppearance = */ 123,
+ )
+ )
+
+ private val defaultRegisterStatusBarResult
+ get() =
+ RegisterStatusBarResult(
+ statusBarIcons,
+ disabledFlags1,
+ appearance,
+ appearanceRegions,
+ imeWindowVis,
+ imeBackDisposition,
+ showImeSwitcher,
+ disabledFlags2,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibleTypes,
+ packageName,
+ transientBarTypes,
+ letterboxDetails,
+ )
+
+ private val registerStatusBarResultSecondaryDisplay
+ get() =
+ RegisterStatusBarResult(
+ statusBarIconsSecondaryDisplay,
+ disabledFlags1SecondaryDisplay,
+ appearanceSecondaryDisplay,
+ appearanceRegionsSecondaryDisplay,
+ imeWindowVisSecondaryDisplay,
+ imeBackDispositionSecondaryDisplay,
+ showImeSwitcherSecondaryDisplay,
+ disabledFlags2SecondaryDisplay,
+ navbarColorManagedByImeSecondaryDisplay,
+ behaviorSecondaryDisplay,
+ requestedVisibleTypesSecondaryDisplay,
+ packageNameSecondaryDisplay,
+ transientBarTypesSecondaryDisplay,
+ letterboxDetailsSecondaryDisplay,
+ )
+
override fun expandNotificationsPanel() {}
override fun collapsePanels() {}
@@ -140,21 +225,16 @@ class FakeStatusBarService : IStatusBarService.Stub() {
override fun registerStatusBar(callbacks: IStatusBar): RegisterStatusBarResult {
registeredStatusBar = callbacks
- return RegisterStatusBarResult(
- statusBarIcons,
- disabledFlags1,
- appearance,
- appearanceRegions,
- imeWindowVis,
- imeBackDisposition,
- showImeSwitcher,
- disabledFlags2,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- transientBarTypes,
- letterboxDetails,
+ return defaultRegisterStatusBarResult
+ }
+
+ override fun registerStatusBarForAllDisplays(
+ callbacks: IStatusBar
+ ): Map<String, RegisterStatusBarResult> {
+ registeredStatusBar = callbacks
+ return mapOf(
+ DEFAULT_DISPLAY_ID.toString() to defaultRegisterStatusBarResult,
+ SECONDARY_DISPLAY_ID.toString() to registerStatusBarResultSecondaryDisplay,
)
}
@@ -352,4 +432,9 @@ class FakeStatusBarService : IStatusBarService.Stub() {
override fun unregisterNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
override fun showRearDisplayDialog(currentBaseState: Int) {}
+
+ companion object {
+ const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
+ const val SECONDARY_DISPLAY_ID = 2
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt
new file mode 100644
index 000000000000..470a8e4b6ef2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.biometrics
+
+import android.hardware.fingerprint.FingerprintManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.fingerprintManager by Kosmos.Fixture { mock<FingerprintManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
index ae592b968f8b..646c19086a59 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
@@ -17,20 +17,19 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.applicationContext
-import android.hardware.fingerprint.FingerprintManager
import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.fingerprintManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
-import com.android.systemui.util.mockito.mock
val Kosmos.udfpsOverlayInteractor by Fixture {
UdfpsOverlayInteractor(
context = applicationContext,
authController = authController,
selectedUserInteractor = selectedUserInteractor,
- fingerprintManager = mock<FingerprintManager>(),
+ fingerprintManager = fingerprintManager,
scope = applicationCoroutineScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index 4d74254cf9f8..487049740079 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.common.ui.data.repository
import android.content.res.Configuration
+import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -25,6 +26,7 @@ import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -46,6 +48,10 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor
override val configurationValues: Flow<Configuration> =
_configurationChangeValues.asSharedFlow()
+ private val _onMovedToDisplay = MutableStateFlow<Int>(Display.DEFAULT_DISPLAY)
+ override val onMovedToDisplay: StateFlow<Int>
+ get() = _onMovedToDisplay
+
private val _scaleForResolution = MutableStateFlow(1f)
override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
@@ -64,6 +70,10 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor
onAnyConfigurationChange()
}
+ fun onMovedToDisplay(newDisplayId: Int) {
+ _onMovedToDisplay.value = newDisplayId
+ }
+
fun setScaleForResolution(scale: Float) {
_scaleForResolution.value = scale
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 1f68195a9acc..ad92b318b0b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.domain.interactor
+import android.content.testableContext
import android.os.userManager
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.communalMediaRepository
@@ -34,6 +35,7 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.fakeManagedProfileController
@@ -67,6 +69,13 @@ val Kosmos.communalInteractor by Fixture {
val Kosmos.editWidgetsActivityStarter by Fixture<EditWidgetsActivityStarter> { mock() }
+fun Kosmos.setCommunalV2ConfigEnabled(enabled: Boolean) {
+ testableContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ enabled,
+ )
+}
+
suspend fun Kosmos.setCommunalEnabled(enabled: Boolean) {
fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, enabled)
if (enabled) {
@@ -76,6 +85,15 @@ suspend fun Kosmos.setCommunalEnabled(enabled: Boolean) {
}
}
+suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean) {
+ setCommunalV2ConfigEnabled(true)
+ if (enabled) {
+ fakeUserRepository.asMainUser()
+ } else {
+ fakeUserRepository.asDefaultUser()
+ }
+}
+
suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
setCommunalEnabled(available)
with(fakeKeyguardRepository) {
@@ -83,3 +101,12 @@ suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
setKeyguardShowing(available)
}
}
+
+suspend fun Kosmos.setCommunalV2Available(available: Boolean) {
+ setCommunalV2ConfigEnabled(true)
+ setCommunalEnabled(available)
+ with(fakeKeyguardRepository) {
+ setIsEncryptedOrLockdown(!available)
+ setKeyguardShowing(available)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
new file mode 100644
index 000000000000..b407b1ba227a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import android.service.dream.dreamManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.policy.batteryController
+
+val Kosmos.communalToDreamButtonViewModel by
+ Kosmos.Fixture {
+ CommunalToDreamButtonViewModel(
+ backgroundContext = testDispatcher,
+ batteryController = batteryController,
+ dreamManager = dreamManager,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
index 1df3ef48d5a7..1021169c4b3b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
@@ -17,9 +17,11 @@
package com.android.systemui.education.data.repository
import com.android.systemui.kosmos.Kosmos
+import java.time.Duration
import java.time.Instant
var Kosmos.contextualEducationRepository: FakeContextualEducationRepository by
Kosmos.Fixture { FakeContextualEducationRepository() }
-var Kosmos.fakeEduClock: FakeEduClock by Kosmos.Fixture { FakeEduClock(Instant.MIN) }
+var Kosmos.fakeEduClock: FakeEduClock by
+ Kosmos.Fixture { FakeEduClock(Instant.ofEpochSecond(Duration.ofDays(30).seconds)) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt
new file mode 100644
index 000000000000..63bfa52e9720
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.globalactions
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Provides a mock */
+val Kosmos.globalActionsDialogLite: GlobalActionsDialogLite by Kosmos.Fixture { mock() }
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 1828da5c0790..2641070a1a59 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
@@ -22,11 +22,14 @@ import android.content.res.mainResources
import android.hardware.input.fakeInputManager
import android.view.windowManager
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.keyboard.shortcut.data.repository.AppLaunchDataRepository
import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
+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.ShortcutHelperInputDeviceRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
@@ -38,6 +41,7 @@ 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.ShortcutHelperStateInteractor
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperExclusions
import com.android.systemui.keyboard.shortcut.ui.ShortcutCustomizationDialogStarter
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
@@ -45,6 +49,7 @@ import com.android.systemui.keyguard.data.repository.fakeCommandQueue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
@@ -80,12 +85,16 @@ var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by
var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
+val Kosmos.shortcutHelperExclusions by
+ Kosmos.Fixture { ShortcutHelperExclusions(applicationContext) }
+
val Kosmos.shortcutCategoriesUtils by
Kosmos.Fixture {
ShortcutCategoriesUtils(
applicationContext,
backgroundCoroutineContext,
fakeInputManager.inputManager,
+ shortcutHelperExclusions,
)
}
@@ -93,35 +102,54 @@ val Kosmos.defaultShortcutCategoriesRepository by
Kosmos.Fixture {
DefaultShortcutCategoriesRepository(
applicationCoroutineScope,
- testDispatcher,
shortcutHelperSystemShortcutsSource,
shortcutHelperMultiTaskingShortcutsSource,
shortcutHelperAppCategoriesShortcutsSource,
shortcutHelperInputShortcutsSource,
shortcutHelperCurrentAppShortcutsSource,
- fakeInputManager.inputManager,
- shortcutHelperStateRepository,
+ shortcutHelperInputDeviceRepository,
shortcutCategoriesUtils,
)
}
val Kosmos.inputGestureMaps by Kosmos.Fixture { InputGestureMaps(applicationContext) }
-val Kosmos.customInputGesturesRepository by Kosmos.Fixture {
- CustomInputGesturesRepository(userTracker, testDispatcher)
-}
+val Kosmos.inputGestureDataAdapter by
+ Kosmos.Fixture { InputGestureDataAdapter(userTracker, inputGestureMaps, applicationContext) }
+
+val Kosmos.customInputGesturesRepository by
+ Kosmos.Fixture { CustomInputGesturesRepository(userTracker, testDispatcher) }
+
+val Kosmos.shortcutHelperInputDeviceRepository by
+ Kosmos.Fixture {
+ ShortcutHelperInputDeviceRepository(
+ shortcutHelperStateRepository,
+ backgroundScope,
+ backgroundCoroutineContext,
+ fakeInputManager.inputManager,
+ )
+ }
+
+val Kosmos.appLaunchDataRepository by
+ Kosmos.Fixture {
+ AppLaunchDataRepository(
+ fakeInputManager.inputManager,
+ backgroundScope,
+ shortcutCategoriesUtils,
+ shortcutHelperInputDeviceRepository,
+ )
+ }
val Kosmos.customShortcutCategoriesRepository by
Kosmos.Fixture {
CustomShortcutCategoriesRepository(
- shortcutHelperStateRepository,
+ shortcutHelperInputDeviceRepository,
applicationCoroutineScope,
- testDispatcher,
shortcutCategoriesUtils,
- applicationContext,
- inputGestureMaps,
+ inputGestureDataAdapter,
customInputGesturesRepository,
- fakeInputManager.inputManager
+ fakeInputManager.inputManager,
+ appLaunchDataRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
index 3304d44db9b1..10ca84f579d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
@@ -19,11 +19,9 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.data.repository.powerRepository
import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.dozeInteractor: DozeInteractor by Fixture {
- DozeInteractor(
- keyguardRepository,
- { sceneInteractor },
- )
+ DozeInteractor(keyguardRepository, powerRepository, { sceneInteractor })
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
index 2d1f836d455d..b03624bcc294 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -26,5 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.alternateBouncerToPrimaryBouncerTransitionViewModel by Fixture {
AlternateBouncerToPrimaryBouncerTransitionViewModel(
animationFlow = keyguardTransitionAnimationFlow,
+ shadeDependentFlows = shadeDependentFlows,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt
new file mode 100644
index 000000000000..5e6d605e9bfb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.aodToPrimaryBouncerTransitionViewModel by Fixture {
+ AodToPrimaryBouncerTransitionViewModel(animationFlow = keyguardTransitionAnimationFlow)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..09233af7bae6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.primaryBouncerToGlanceableHubTransitionViewModel by Fixture {
+ PrimaryBouncerToGlanceableHubTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
index 370afc3b660b..76478cb43361 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
@@ -26,5 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture {
PrimaryBouncerToLockscreenTransitionViewModel(
animationFlow = keyguardTransitionAnimationFlow,
+ shadeDependentFlows = shadeDependentFlows,
)
}
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 1556058d51ba..d941fb049835 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
@@ -8,7 +8,10 @@ import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
import com.android.systemui.util.mockito.mock
import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -51,10 +54,34 @@ var Kosmos.brightnessWarningToast: BrightnessWarningToast by
* that kosmos instance
*/
fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
- testScope.runTest { this@runTest.testBody() }
+ testScope.runTest testBody@{ this@runTest.testBody() }
fun Kosmos.runCurrent() = testScope.runCurrent()
fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow)
fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow)
+
+/**
+ * Retrieve the current value of this [StateFlow] safely. Needs a [TestScope] in order to make sure
+ * that all pending tasks have run before returning a value. Tests that directly access
+ * [StateFlow.value] may be incorrect, since the value returned may be stale if the current test
+ * dispatcher is a [StandardTestDispatcher].
+ *
+ * If you want to assert on a [Flow] that is not a [StateFlow], please use
+ * [TestScope.collectLastValue], to make sure that the desired value is captured when emitted.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.currentValue(stateFlow: StateFlow<T>): T {
+ val values = mutableListOf<T>()
+ val job = backgroundScope.launch { stateFlow.collect(values::add) }
+ runCurrent()
+ job.cancel()
+ // StateFlow should always have at least one value
+ return values.last()
+}
+
+/** Retrieve the current value of this [StateFlow] safely. See `currentValue(TestScope)`. */
+fun <T> Kosmos.currentValue(stateFlow: StateFlow<T>): T {
+ return testScope.currentValue(stateFlow)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
index 7a04aa288dce..7964c1114be5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
@@ -19,7 +19,6 @@ package com.android.systemui.media.controls.data.repository
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.media.controls.util.mediaSmartspaceLogger
-import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.util.time.systemClock
val Kosmos.mediaFilterRepository by
@@ -27,7 +26,6 @@ val Kosmos.mediaFilterRepository by
MediaFilterRepository(
applicationContext = applicationContext,
systemClock = systemClock,
- configurationController = configurationController,
smartspaceLogger = mediaSmartspaceLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
index 0ec8d49ec29b..49bbbef6ccc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
@@ -17,6 +17,6 @@
package com.android.systemui.plugins
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index 10b073e8f331..2e6d8ed5aa5b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -28,6 +28,7 @@ import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.mockito.mock
@@ -49,3 +50,6 @@ var Kosmos.statusBarStateController: SysuiStatusBarStateController by
{ alternateBouncerInteractor },
)
}
+
+var Kosmos.fakeStatusBarStateController: SysuiStatusBarStateController by
+ Kosmos.Fixture { FakeStatusBarStateController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
index ace650020a9d..9dfbcfbbc407 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -19,6 +19,7 @@ package com.android.systemui.power.data.repository
import android.os.PowerManager
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.shared.model.DozeScreenStateModel
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
@@ -41,6 +42,8 @@ class FakePowerRepository @Inject constructor() : PowerRepository {
private val _screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_OFF)
override val screenPowerState = _screenPowerState.asStateFlow()
+ override val dozeScreenState = MutableStateFlow(DozeScreenStateModel.UNKNOWN)
+
var lastWakeWhy: String? = null
var lastWakeReason: Int? = null
@@ -63,7 +66,7 @@ class FakePowerRepository @Inject constructor() : PowerRepository {
rawState: WakefulnessState,
lastWakeReason: WakeSleepReason,
lastSleepReason: WakeSleepReason,
- powerButtonLaunchGestureTriggered: Boolean
+ powerButtonLaunchGestureTriggered: Boolean,
) {
_wakefulness.value =
WakefulnessModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index c574463eb258..49957f0b43cc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.qs.composefragment.viewmodel
import android.content.res.mainResources
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -33,6 +34,7 @@ import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteracto
import com.android.systemui.qs.panels.ui.viewmodel.inFirstPageViewModel
import com.android.systemui.qs.panels.ui.viewmodel.mediaInRowInLandscapeViewModelFactory
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
@@ -56,6 +58,7 @@ val Kosmos.qsFragmentComposeViewModelFactory by
disableFlagsInteractor,
keyguardTransitionInteractor,
largeScreenShadeInterpolator,
+ shadeInteractor,
configurationInteractor,
largeScreenHeaderHelper,
tileSquishinessInteractor,
@@ -66,6 +69,7 @@ val Kosmos.qsFragmentComposeViewModelFactory by
qqsMediaHost,
qsMediaHost,
usingMediaInComposeFragment,
+ uiEventLoggerFake,
lifecycleScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
index 4acedaa9044d..322b412fb054 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
@@ -18,5 +18,4 @@ package com.android.systemui.qs.panels.data.repository
import com.android.systemui.kosmos.Kosmos
-var Kosmos.gridLayoutTypeRepository: GridLayoutTypeRepository by
- Kosmos.Fixture { GridLayoutTypeRepositoryImpl() }
+var Kosmos.gridLayoutTypeRepository by Kosmos.Fixture { GridLayoutTypeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
index c9516429553b..40c3c96ff241 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
@@ -20,10 +20,19 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import com.android.systemui.qs.panels.ui.compose.GridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.paginatedGridLayout
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
val Kosmos.gridLayoutTypeInteractor by
- Kosmos.Fixture { GridLayoutTypeInteractor(gridLayoutTypeRepository) }
+ Kosmos.Fixture { GridLayoutTypeInteractor(gridLayoutTypeRepository, shadeModeInteractor) }
val Kosmos.gridLayoutMap: Map<GridLayoutType, GridLayout> by
- Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridLayout)) }
+ Kosmos.Fixture {
+ mapOf(
+ Pair(InfiniteGridLayoutType, infiniteGridLayout),
+ Pair(PaginatedGridLayoutType, paginatedGridLayout),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt
new file mode 100644
index 000000000000..f412d98a9d4f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.panels.ui.compose
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.paginatedGridViewModelFactory
+
+val Kosmos.paginatedGridLayout by
+ Kosmos.Fixture { PaginatedGridLayout(paginatedGridViewModelFactory, infiniteGridLayout) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt
index 6fe860cfd0d3..f57806c8b678 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.qs.panels.ui.compose.infinitegrid
import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
index 33227a4fcc62..71408f6adffd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
@@ -17,14 +17,16 @@
package com.android.systemui.qs.panels.ui.viewmodel
import android.content.applicationContext
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor
import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
import com.android.systemui.qs.panels.domain.interactor.tilesAvailabilityInteractor
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor
@@ -35,10 +37,12 @@ val Kosmos.editModeViewModel by
currentTilesInteractor,
tilesAvailabilityInteractor,
minimumTilesInteractor,
+ uiEventLoggerFake,
configurationInteractor,
applicationContext,
infiniteGridLayout,
applicationCoroutineScope,
+ testDispatcher,
gridLayoutTypeInteractor,
gridLayoutMap,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 5c71ba2f8bbd..128cfcad5c45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.editModeButtonViewModelFactory
val Kosmos.paginatedGridViewModel by
Kosmos.Fixture {
@@ -33,3 +34,10 @@ val Kosmos.paginatedGridViewModel by
falsingInteractor,
)
}
+
+val Kosmos.paginatedGridViewModelFactory by
+ Kosmos.Fixture {
+ object : PaginatedGridViewModel.Factory {
+ override fun create() = paginatedGridViewModel
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
index 9481fcac97d6..e79d213a8ffb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
@@ -20,7 +20,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
val Kosmos.tileGridViewModel by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt
index b8d3ff425f20..8ae1332c387a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.qs.panels.ui.viewmodel.toolbar
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
val Kosmos.editModeButtonViewModelFactory by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
new file mode 100644
index 000000000000..52d8a3aac836
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.panels.ui.viewmodel.toolbar
+
+import android.content.applicationContext
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.globalactions.globalActionsDialogLite
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.footerActionsInteractor
+
+val Kosmos.toolbarViewModelFactory by
+ Kosmos.Fixture {
+ object : ToolbarViewModel.Factory {
+ override fun create(): ToolbarViewModel {
+ return ToolbarViewModel(
+ editModeButtonViewModelFactory,
+ footerActionsInteractor,
+ { globalActionsDialogLite },
+ falsingInteractor,
+ applicationContext,
+ )
+ }
+ }
+ }
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/interactor/FakeQSTileUserActionInteractor.kt
index 597d52dcb299..bc1c60c33d71 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/interactor/FakeQSTileUserActionInteractor.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.base.interactor
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.FakeTileDetailsViewModel
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -31,4 +33,7 @@ class FakeQSTileUserActionInteractor<T> : QSTileUserActionInteractor<T> {
override suspend fun handleInput(input: QSTileInput<T>) {
mutex.withLock { mutableInputs.add(input) }
}
+
+ override var detailsViewModel: TileDetailsViewModel? =
+ FakeTileDetailsViewModel("FakeQSTileUserActionInteractor")
}
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 2ecfb454a6f0..3c37101cfe66 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,6 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
import javax.inject.Provider
@@ -26,5 +27,6 @@ val Kosmos.modesTileUserActionInteractor: ModesTileUserActionInteractor by
ModesTileUserActionInteractor(
qsTileIntentUserInputHandler,
Provider { modesDialogDelegate }.get(),
+ zenModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
index 6afc0d803f8d..aa6ce9a433df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModelFactory
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.toolbarViewModelFactory
val Kosmos.quickSettingsContainerViewModelFactory by
Kosmos.Fixture {
@@ -36,6 +37,7 @@ val Kosmos.quickSettingsContainerViewModelFactory by
tileGridViewModel,
editModeViewModel,
detailsViewModel,
+ toolbarViewModelFactory,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
index 5d146fbffaca..3d45a51b9f3b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
@@ -29,7 +29,7 @@ import com.android.systemui.util.time.systemClock
/** This factory creates empty mocks. */
var Kosmos.brightnessSliderControllerFactory by
Kosmos.Fixture<BrightnessSliderController.Factory> {
- BrightnessSliderController.Factory(
+ BrightnessSliderController.BrightnessSliderControllerFactory(
falsingManager,
uiEventLogger,
vibratorHelper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index 6944e6c14096..ab193d294b8c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -261,7 +261,7 @@ class ShadeTestUtilSceneImpl(
}
private fun setIdleScene(scene: SceneKey) {
- sceneInteractor.changeScene(scene, "test")
+ sceneInteractor.changeScene(scene, "ShadeTestUtil.setIdleScene")
val transitionState =
MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene))
sceneInteractor.setTransitionState(transitionState)
@@ -274,7 +274,7 @@ class ShadeTestUtilSceneImpl(
progress: Float,
isInitiatedByUserInput: Boolean = true,
) {
- sceneInteractor.changeScene(from, "test")
+ sceneInteractor.changeScene(from, "ShadeTestUtil.setTransitionProgress")
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index 7488397dde1f..636cb37adf03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -16,20 +16,53 @@
package com.android.systemui.shade.data.repository
-import android.view.Display
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
+import com.android.systemui.shade.display.DefaultDisplayShadePolicy
import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
+import com.android.systemui.util.settings.fakeGlobalSettings
-val Kosmos.defaultShadeDisplayPolicy: ShadeDisplayPolicy by
- Kosmos.Fixture { SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) }
+val Kosmos.defaultShadeDisplayPolicy: DefaultDisplayShadePolicy by
+ Kosmos.Fixture { DefaultDisplayShadePolicy() }
+
+val Kosmos.anyExternalShadeDisplayPolicy: AnyExternalShadeDisplayPolicy by
+ Kosmos.Fixture {
+ AnyExternalShadeDisplayPolicy(
+ bgScope = testScope.backgroundScope,
+ displayRepository = displayRepository,
+ )
+ }
+
+val Kosmos.focusBasedShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
+ Kosmos.Fixture {
+ StatusBarTouchShadeDisplayPolicy(
+ displayRepository = displayRepository,
+ backgroundScope = testScope.backgroundScope,
+ keyguardRepository = keyguardRepository,
+ shadeOnDefaultDisplayWhenLocked = false,
+ )
+ }
val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
Kosmos.Fixture {
ShadeDisplaysRepositoryImpl(
- defaultPolicy = defaultShadeDisplayPolicy,
bgScope = testScope.backgroundScope,
+ globalSettings = fakeGlobalSettings,
+ policies = shadeDisplayPolicies,
+ defaultPolicy = defaultShadeDisplayPolicy,
+ )
+ }
+
+val Kosmos.shadeDisplayPolicies: Set<ShadeDisplayPolicy> by
+ Kosmos.Fixture {
+ setOf(
+ defaultShadeDisplayPolicy,
+ anyExternalShadeDisplayPolicy,
+ focusBasedShadeDisplayPolicy,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
index db4df38e038a..f2af619a4ad7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
@@ -17,25 +17,24 @@
package com.android.systemui.shade.domain.interactor
import android.content.mockedContext
-import android.view.mockWindowManager
+import android.window.WindowContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.ui.view.mockShadeRootView
import com.android.systemui.shade.ShadeWindowLayoutParams
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import java.util.Optional
+import org.mockito.kotlin.mock
-val Kosmos.shadeLayoutParams by Kosmos.Fixture {
- ShadeWindowLayoutParams.create(mockedContext)
-}
+val Kosmos.shadeLayoutParams by Kosmos.Fixture { ShadeWindowLayoutParams.create(mockedContext) }
+
+val Kosmos.mockedWindowContext by Kosmos.Fixture { mock<WindowContext>() }
val Kosmos.shadeDisplaysInteractor by
Kosmos.Fixture {
ShadeDisplaysInteractor(
Optional.of(mockShadeRootView),
fakeShadeDisplaysRepository,
- mockedContext,
- shadeLayoutParams,
- mockWindowManager,
+ mockedWindowContext,
testScope.backgroundScope,
testScope.backgroundScope.coroutineContext,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
index e5a75d59468d..8865573c4030 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 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.
@@ -18,8 +18,14 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeKeyguardTransitionController by Fixture {
+ mock<LockscreenShadeKeyguardTransitionController>()
+}
var Kosmos.lockscreenShadeKeyguardTransitionControllerFactory by Fixture {
- mock<LockscreenShadeKeyguardTransitionController.Factory>()
+ LockscreenShadeKeyguardTransitionController.Factory {
+ lockscreenShadeKeyguardTransitionController
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
index 27679804d11f..fc52e454a1c6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
@@ -18,8 +18,12 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeQsTransitionController by Fixture {
+ mock<LockscreenShadeQsTransitionController>()
+}
var Kosmos.lockscreenShadeQsTransitionControllerFactory by Fixture {
- mock<LockscreenShadeQsTransitionController.Factory>()
+ LockscreenShadeQsTransitionController.Factory { lockscreenShadeQsTransitionController }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
index 43e39c05f6e9..5523bd68f692 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
@@ -18,8 +18,12 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.singleShadeLockScreenOverScroller by Fixture {
+ mock<SingleShadeLockScreenOverScroller>()
+}
var Kosmos.singleShadeLockScreenOverScrollerFactory by Fixture {
- mock<SingleShadeLockScreenOverScroller.Factory>()
+ SingleShadeLockScreenOverScroller.Factory { _ -> singleShadeLockScreenOverScroller }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
index 017371a6cba8..e491dffb0ed5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.splitShadeLockScreenOverScroller by Fixture { mock<SplitShadeLockScreenOverScroller>() }
var Kosmos.splitShadeLockScreenOverScrollerFactory by Fixture {
- mock<SplitShadeLockScreenOverScroller.Factory>()
+ SplitShadeLockScreenOverScroller.Factory { _, _ -> splitShadeLockScreenOverScroller }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
index 4bcce8601d64..d0c80c7332b3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -19,8 +19,13 @@ package com.android.systemui.statusbar.chips.notification.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
val Kosmos.notifChipsViewModel: NotifChipsViewModel by
Kosmos.Fixture {
- NotifChipsViewModel(applicationCoroutineScope, statusBarNotificationChipsInteractor)
+ NotifChipsViewModel(
+ applicationCoroutineScope,
+ statusBarNotificationChipsInteractor,
+ headsUpNotificationInteractor,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 8712b02ec884..3c2d004fcad7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -34,12 +34,12 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor
const val DISPLAY_ID = Display.DEFAULT_DISPLAY
}
- override val defaultDisplay: FakeStatusBarModePerDisplayRepository =
- FakeStatusBarModePerDisplayRepository()
+ private val perDisplayRepos = mutableMapOf<Int, FakeStatusBarModePerDisplayRepository>()
- override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
- return defaultDisplay
- }
+ override val defaultDisplay: FakeStatusBarModePerDisplayRepository = forDisplay(DISPLAY_ID)
+
+ override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository =
+ perDisplayRepos.computeIfAbsent(displayId) { FakeStatusBarModePerDisplayRepository() }
}
class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository {
@@ -47,6 +47,7 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
override val isInFullscreenMode = MutableStateFlow(false)
override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT)
+ override val ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
override fun showTransient() {
isTransientShown.value = true
@@ -59,6 +60,9 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
override fun start() {}
override fun stop() {}
+ override fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean) {
+ ongoingProcessRequiresStatusBarVisible.value = requiredVisible
+ }
override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
new file mode 100644
index 000000000000..72165c95fc55
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.gesture
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler by
+Kosmos.Fixture {
+ mock<SwipeStatusBarAwayGestureHandler>()
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt
new file mode 100644
index 000000000000..fa5bd7a90fcf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
+import org.mockito.kotlin.mock
+
+var Kosmos.dynamicPrivacyController: DynamicPrivacyController by
+ Kosmos.Fixture {
+ DynamicPrivacyController(
+ notificationLockscreenUserManager,
+ keyguardStateController,
+ statusBarStateController,
+ )
+ }
+
+var Kosmos.mockDynamicPrivacyController: DynamicPrivacyController by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
new file mode 100644
index 000000000000..360e9e93f18d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.notification
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
+import org.mockito.kotlin.mock
+
+val Kosmos.visualInterruptionDecisionProvider by
+ Kosmos.Fixture { mock<VisualInterruptionDecisionProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt
new file mode 100644
index 000000000000..88bf9a5f2d5b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.notification.collection.coordinator
+
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.statusbar.policy.sensitiveNotificationProtectionController
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+var Kosmos.sensitiveContentCoordinator: SensitiveContentCoordinator by
+ Kosmos.Fixture {
+ SensitiveContentCoordinatorImpl(
+ dynamicPrivacyController,
+ notificationLockscreenUserManager,
+ keyguardUpdateMonitor,
+ statusBarStateController,
+ keyguardStateController,
+ selectedUserInteractor,
+ sensitiveNotificationProtectionController,
+ deviceEntryInteractor,
+ sceneInteractor,
+ testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
index 7de22d8c8b43..4a692d2ca4d9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
@@ -26,7 +26,8 @@ class FakeHeadsUpRowRepository(override val key: String, override val elementKey
elementKey: Any = Any(),
isPinned: Boolean,
) : this(key = key, elementKey = elementKey) {
- this.pinnedStatus.value = if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned
+ this.pinnedStatus.value =
+ if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned
}
constructor(
@@ -40,3 +41,10 @@ class FakeHeadsUpRowRepository(override val key: String, override val elementKey
override val pinnedStatus: MutableStateFlow<PinnedStatus> =
MutableStateFlow(PinnedStatus.NotPinned)
}
+
+/** Use this fake if you're using [UnconfinedTestDispatcher]. See b/383528592 for reasoning. */
+class UnconfinedFakeHeadsUpRowRepository(
+ override val key: String,
+ override val elementKey: Any = Any(),
+ override val pinnedStatus: MutableStateFlow<PinnedStatus>,
+) : HeadsUpRowRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
new file mode 100644
index 000000000000..768952d1ee77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.notification.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.notificationAlertsInteractor by Kosmos.Fixture { mock<NotificationAlertsInteractor>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
index 51fb36fb2ead..40d91017eeef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
@@ -20,7 +20,10 @@ import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
Kosmos.Fixture {
@@ -28,6 +31,9 @@ val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
scope = applicationCoroutineScope,
activeNotificationsInteractor = activeNotificationsInteractor,
activityManagerRepository = activityManagerRepository,
+ statusBarModeRepositoryStore = fakeStatusBarModeRepository,
+ statusBarWindowControllerStore = fakeStatusBarWindowControllerStore,
+ swipeStatusBarAwayGestureHandler = swipeStatusBarAwayGestureHandler,
logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
index 3963d7c5be63..766b280334c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
@@ -23,5 +23,6 @@ import com.android.systemui.statusbar.StatusBarIconView
fun inCallModel(
startTimeMs: Long,
notificationIcon: StatusBarIconView? = null,
- intent: PendingIntent? = null
-) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent)
+ intent: PendingIntent? = null,
+ notificationKey: String = "test",
+) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent, notificationKey)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 32191277c94a..13673d16bf3c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -27,6 +27,10 @@ class FakeConfigurationController @Inject constructor() :
listeners.forEach { it.onConfigChanged(newConfiguration) }
}
+ override fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration) {
+ listeners.forEach { it.onMovedToDisplay(newDisplayId, newConfiguration) }
+ }
+
override fun notifyThemeChanged() {
listeners.forEach { it.onThemeChanged() }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
index f19ac1e5a58d..26642d4f7534 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.policy
import com.android.systemui.kosmos.Kosmos
-import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
var Kosmos.keyguardStateController: KeyguardStateController by
- Kosmos.Fixture { mock(KeyguardStateController::class.java) }
+ Kosmos.Fixture { mock<KeyguardStateController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt
new file mode 100644
index 000000000000..8f9184d1dbf3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.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.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+var Kosmos.sensitiveNotificationProtectionController: SensitiveNotificationProtectionController by
+ Kosmos.Fixture { mockSensitiveNotificationProtectionController }
+val Kosmos.mockSensitiveNotificationProtectionController:
+ SensitiveNotificationProtectionController by
+ Kosmos.Fixture { mock<SensitiveNotificationProtectionController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index 68d08e285c53..bbccbb10923a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -20,6 +20,7 @@ import android.content.applicationContext
import com.android.settingslib.notification.modes.zenIconLoader
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository
import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
@@ -32,6 +33,7 @@ val Kosmos.zenModeInteractor by Fixture {
zenModeRepository = zenModeRepository,
notificationSettingsRepository = notificationSettingsRepository,
bgDispatcher = testDispatcher,
+ backgroundScope = backgroundScope,
iconLoader = zenIconLoader,
deviceProvisioningRepository = deviceProvisioningRepository,
userSetupRepository = userSetupRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
index a110a49ccba8..09e6a0eaa301 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
@@ -20,6 +20,7 @@ import android.view.View
import android.view.ViewGroup
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.fragments.FragmentHostManager
+import kotlinx.coroutines.flow.MutableStateFlow
import java.util.Optional
class FakeStatusBarWindowController : StatusBarWindowController {
@@ -30,6 +31,8 @@ class FakeStatusBarWindowController : StatusBarWindowController {
var isStopped = false
private set
+ val ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
+
override val statusBarHeight: Int = 0
override fun refreshStatusBarHeight() {}
@@ -57,5 +60,7 @@ class FakeStatusBarWindowController : StatusBarWindowController {
override fun setForceStatusBarVisible(forceStatusBarVisible: Boolean) {}
- override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {}
+ override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {
+ ongoingProcessRequiresStatusBarVisible.value = visible
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
index f12089a08488..e767b8775a42 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
@@ -20,9 +20,9 @@ import android.view.MotionEvent
import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
-class FakeVelocityTracker : VelocityTracker {
+class FakeVelocityTracker(velocity: Float = 0f) : VelocityTracker {
- private var fakeVelocity = Velocity(0f)
+ private var fakeVelocity = Velocity(velocity)
override fun calculateVelocity(): Velocity {
return fakeVelocity
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index 111c40d49efc..9cf25e8df727 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -16,6 +16,8 @@ package com.android.systemui.utils.leaks;
import android.content.res.Configuration;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.policy.ConfigurationController;
public class FakeConfigurationController
@@ -43,4 +45,10 @@ public class FakeConfigurationController
public String getNightModeName() {
return "undefined";
}
+
+ @Override
+ public void dispatchOnMovedToDisplay(int newDisplayId,
+ @NonNull Configuration newConfiguration) {
+
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
index c8ba551c518a..34661ced71b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor
import com.android.systemui.volume.dialog.shared.volumeDialogLogger
@@ -31,7 +32,8 @@ val Kosmos.volumeDialogRingerDrawerViewModel by
applicationContext = applicationContext,
backgroundDispatcher = testDispatcher,
coroutineScope = applicationCoroutineScope,
- interactor = volumeDialogRingerInteractor,
+ soundPolicyInteractor = notificationsSoundPolicyInteractor,
+ ringerInteractor = volumeDialogRingerInteractor,
vibrator = vibratorHelper,
volumeDialogLogger = volumeDialogLogger,
visibilityInteractor = volumeDialogVisibilityInteractor,
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
index 8bf3a43765ae..ae9b8c85910f 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
@@ -17,6 +17,7 @@
package com.android.systemui.kairos
import com.android.systemui.kairos.util.These
+import com.android.systemui.kairos.util.WithPrev
import com.android.systemui.kairos.util.just
import com.android.systemui.kairos.util.none
import kotlinx.coroutines.flow.Flow
@@ -27,15 +28,18 @@ import kotlinx.coroutines.flow.conflate
* Returns a [TFlow] that emits the value sampled from the [Transactional] produced by each emission
* of the original [TFlow], within the same transaction of the original emission.
*/
+@ExperimentalFrpApi
fun <A> TFlow<Transactional<A>>.sampleTransactionals(): TFlow<A> = map { it.sample() }
/** @see FrpTransactionScope.sample */
+@ExperimentalFrpApi
fun <A, B, C> TFlow<A>.sample(
state: TState<B>,
transform: suspend FrpTransactionScope.(A, B) -> C,
): TFlow<C> = map { transform(it, state.sample()) }
/** @see FrpTransactionScope.sample */
+@ExperimentalFrpApi
fun <A, B, C> TFlow<A>.sample(
transactional: Transactional<B>,
transform: suspend FrpTransactionScope.(A, B) -> C,
@@ -50,6 +54,7 @@ fun <A, B, C> TFlow<A>.sample(
*
* @see sample
*/
+@ExperimentalFrpApi
fun <A, B, C> TFlow<A>.samplePromptly(
state: TState<B>,
transform: suspend FrpTransactionScope.(A, B) -> C,
@@ -70,19 +75,10 @@ fun <A, B, C> TFlow<A>.samplePromptly(
}
/**
- * Returns a [TState] containing a map with a snapshot of the current state of each [TState] in the
- * original map.
- */
-fun <K, A> Map<K, TState<A>>.combineValues(): TState<Map<K, A>> =
- asIterable()
- .map { (k, state) -> state.map { v -> k to v } }
- .combine()
- .map { entries -> entries.toMap() }
-
-/**
* Returns a cold [Flow] that, when collected, emits from this [TFlow]. [network] is needed to
* transactionally connect to / disconnect from the [TFlow] when collection starts/stops.
*/
+@ExperimentalFrpApi
fun <A> TFlow<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
@@ -90,6 +86,7 @@ fun <A> TFlow<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
* Returns a cold [Flow] that, when collected, emits from this [TState]. [network] is needed to
* transactionally connect to / disconnect from the [TState] when collection starts/stops.
*/
+@ExperimentalFrpApi
fun <A> TState<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
@@ -99,6 +96,7 @@ fun <A> TState<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
*
* When collection is cancelled, so is the [FrpSpec]. This means all ongoing work is cleaned up.
*/
+@ExperimentalFrpApi
@JvmName("flowSpecToColdConflatedFlow")
fun <A> FrpSpec<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
@@ -109,6 +107,7 @@ fun <A> FrpSpec<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
*
* When collection is cancelled, so is the [FrpSpec]. This means all ongoing work is cleaned up.
*/
+@ExperimentalFrpApi
@JvmName("stateSpecToColdConflatedFlow")
fun <A> FrpSpec<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
@@ -117,6 +116,7 @@ fun <A> FrpSpec<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
* Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
* this [network], and then emits from the returned [TFlow].
*/
+@ExperimentalFrpApi
@JvmName("transactionalFlowToColdConflatedFlow")
fun <A> Transactional<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
@@ -125,6 +125,7 @@ fun <A> Transactional<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A
* Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
* this [network], and then emits from the returned [TState].
*/
+@ExperimentalFrpApi
@JvmName("transactionalStateToColdConflatedFlow")
fun <A> Transactional<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
@@ -135,6 +136,7 @@ fun <A> Transactional<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<
*
* When collection is cancelled, so is the [FrpStateful]. This means all ongoing work is cleaned up.
*/
+@ExperimentalFrpApi
@JvmName("statefulFlowToColdConflatedFlow")
fun <A> FrpStateful<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
@@ -145,11 +147,13 @@ fun <A> FrpStateful<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A>
*
* When collection is cancelled, so is the [FrpStateful]. This means all ongoing work is cleaned up.
*/
+@ExperimentalFrpApi
@JvmName("statefulStateToColdConflatedFlow")
fun <A> FrpStateful<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
/** Return a [TFlow] that emits from the original [TFlow] only when [state] is `true`. */
+@ExperimentalFrpApi
fun <A> TFlow<A>.filter(state: TState<Boolean>): TFlow<A> = filter { state.sample() }
private fun Iterable<Boolean>.allTrue() = all { it }
@@ -157,13 +161,15 @@ private fun Iterable<Boolean>.allTrue() = all { it }
private fun Iterable<Boolean>.anyTrue() = any { it }
/** Returns a [TState] that is `true` only when all of [states] are `true`. */
+@ExperimentalFrpApi
fun allOf(vararg states: TState<Boolean>): TState<Boolean> = combine(*states) { it.allTrue() }
/** Returns a [TState] that is `true` when any of [states] are `true`. */
+@ExperimentalFrpApi
fun anyOf(vararg states: TState<Boolean>): TState<Boolean> = combine(*states) { it.anyTrue() }
/** Returns a [TState] containing the inverse of the Boolean held by the original [TState]. */
-fun not(state: TState<Boolean>): TState<Boolean> = state.mapCheapUnsafe { !it }
+@ExperimentalFrpApi fun not(state: TState<Boolean>): TState<Boolean> = state.mapCheapUnsafe { !it }
/**
* Represents a modal FRP sub-network.
@@ -177,6 +183,7 @@ fun not(state: TState<Boolean>): TState<Boolean> = state.mapCheapUnsafe { !it }
*
* @see FrpStatefulMode
*/
+@ExperimentalFrpApi
fun interface FrpBuildMode<out A> {
/**
* Invoked when this mode is enabled. Returns a value and a [TFlow] that signals a switch to a
@@ -192,6 +199,7 @@ fun interface FrpBuildMode<out A> {
*
* @see FrpBuildMode
*/
+@ExperimentalFrpApi
val <A> FrpBuildMode<A>.compiledFrpSpec: FrpSpec<TState<A>>
get() = frpSpec {
var modeChangeEvents by TFlowLoop<FrpBuildMode<A>>()
@@ -215,6 +223,7 @@ val <A> FrpBuildMode<A>.compiledFrpSpec: FrpSpec<TState<A>>
*
* @see FrpBuildMode
*/
+@ExperimentalFrpApi
fun interface FrpStatefulMode<out A> {
/**
* Invoked when this mode is enabled. Returns a value and a [TFlow] that signals a switch to a
@@ -230,6 +239,7 @@ fun interface FrpStatefulMode<out A> {
*
* @see FrpBuildMode
*/
+@ExperimentalFrpApi
val <A> FrpStatefulMode<A>.compiledStateful: FrpStateful<TState<A>>
get() = statefully {
var modeChangeEvents by TFlowLoop<FrpStatefulMode<A>>()
@@ -246,5 +256,18 @@ val <A> FrpStatefulMode<A>.compiledStateful: FrpStateful<TState<A>>
* Runs [spec] in this [FrpBuildScope], and then re-runs it whenever [rebuildSignal] emits. Returns
* a [TState] that holds the result of the currently-active [FrpSpec].
*/
+@ExperimentalFrpApi
fun <A> FrpBuildScope.rebuildOn(rebuildSignal: TFlow<*>, spec: FrpSpec<A>): TState<A> =
rebuildSignal.map { spec }.holdLatestSpec(spec)
+
+/**
+ * Like [stateChanges] but also includes the old value of this [TState].
+ *
+ * Shorthand for:
+ * ``` kotlin
+ * stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
+ * ```
+ */
+@ExperimentalFrpApi
+val <A> TState<A>.transitions: TFlow<WithPrev<A, A>>
+ get() = stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
index 4de6deb3dc53..209a402bd629 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
@@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.launch
/** A function that modifies the FrpNetwork. */
@@ -596,6 +597,26 @@ interface FrpBuildScope : FrpStateScope {
fun <A> Flow<A>.toTState(initialValue: A): TState<A> = toTFlow().hold(initialValue)
/**
+ * Shorthand for:
+ * ```kotlin
+ * flow.scan(initialValue, operation).toTFlow().hold(initialValue)
+ * ```
+ */
+ @ExperimentalFrpApi
+ fun <A, B> Flow<A>.scanToTState(initialValue: B, operation: (B, A) -> B): TState<B> =
+ scan(initialValue, operation).toTFlow().hold(initialValue)
+
+ /**
+ * Shorthand for:
+ * ```kotlin
+ * flow.scan(initialValue) { a, f -> f(a) }.toTFlow().hold(initialValue)
+ * ```
+ */
+ @ExperimentalFrpApi
+ fun <A> Flow<(A) -> A>.scanToTState(initialValue: A): TState<A> =
+ scanToTState(initialValue) { a, f -> f(a) }
+
+ /**
* Invokes [block] whenever this [TFlow] emits a value. [block] receives an [FrpBuildScope] that
* can be used to make further modifications to the FRP network, and/or perform side-effects via
* [effect].
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
index be2eb4312476..b39dcc131b1d 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
@@ -22,16 +22,17 @@ import kotlinx.coroutines.CoroutineScope
/**
* Scope for external side-effects triggered by the Frp network. This still occurs within the
* context of a transaction, so general suspending calls are disallowed to prevent blocking the
- * transaction. You can use [frpCoroutineScope] to [launch] new coroutines to perform long-running
- * asynchronous work. This scope is alive for the duration of the containing [FrpBuildScope] that
- * this side-effect scope is running in.
+ * transaction. You can use [frpCoroutineScope] to [launch][kotlinx.coroutines.launch] new
+ * coroutines to perform long-running asynchronous work. This scope is alive for the duration of the
+ * containing [FrpBuildScope] that this side-effect scope is running in.
*/
@RestrictsSuspension
@ExperimentalFrpApi
interface FrpEffectScope : FrpTransactionScope {
/**
* A [CoroutineScope] whose lifecycle lives for as long as this [FrpEffectScope] is alive. This
- * is generally until the [Job] returned by [FrpBuildScope.effect] is cancelled.
+ * is generally until the [Job][kotlinx.coroutines.Job] returned by [FrpBuildScope.effect] is
+ * cancelled.
*/
@ExperimentalFrpApi val frpCoroutineScope: CoroutineScope
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
index b688eafe12e9..97252b4a199a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
@@ -137,7 +137,7 @@ internal class LocalFrpNetwork(
override suspend fun <R> transact(block: suspend FrpTransactionScope.() -> R): R {
val result = CompletableDeferred<R>(coroutineContext[Job])
@Suppress("DeferredResultUnused")
- network.transaction {
+ network.transaction("FrpNetwork.transact") {
val buildScope =
BuildScopeImpl(
stateScope = StateScopeImpl(evalScope = this, endSignal = endSignal),
@@ -151,7 +151,7 @@ internal class LocalFrpNetwork(
override suspend fun activateSpec(spec: FrpSpec<*>) {
val job =
network
- .transaction {
+ .transaction("FrpNetwork.activateSpec") {
val buildScope =
BuildScopeImpl(
stateScope = StateScopeImpl(evalScope = this, endSignal = endSignal),
@@ -166,7 +166,8 @@ internal class LocalFrpNetwork(
override fun <In, Out> coalescingMutableTFlow(
coalesce: (old: Out, new: In) -> Out,
getInitialValue: () -> Out,
- ): CoalescingMutableTFlow<In, Out> = CoalescingMutableTFlow(coalesce, network, getInitialValue)
+ ): CoalescingMutableTFlow<In, Out> =
+ CoalescingMutableTFlow(null, coalesce, network, getInitialValue)
override fun <T> mutableTFlow(): MutableTFlow<T> = MutableTFlow(network)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
index 7ba1aca31eae..a175e2e20e46 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
@@ -467,12 +467,12 @@ fun <A> TState<TFlow<A>>.switchPromptly(): TFlow<A> {
@ExperimentalFrpApi
class CoalescingMutableTFlow<In, Out>
internal constructor(
+ internal val name: String?,
internal val coalesce: (old: Out, new: In) -> Out,
internal val network: Network,
private val getInitialValue: () -> Out,
internal val impl: InputNode<Out> = InputNode(),
) : TFlow<Out>() {
- internal val name: String? = null
internal val storage = AtomicReference(false to getInitialValue())
override fun toString(): String = "${this::class.simpleName}@$hashString"
@@ -490,7 +490,7 @@ internal constructor(
val (scheduled, _) = storage.getAndUpdate { (_, old) -> true to coalesce(old, value) }
if (!scheduled) {
@Suppress("DeferredResultUnused")
- network.transaction {
+ network.transaction("CoalescingMutableTFlow${name?.let { "($name)" }.orEmpty()}.emit") {
impl.visit(this, storage.getAndSet(false to getInitialValue()).second)
}
}
@@ -520,16 +520,16 @@ internal constructor(internal val network: Network, internal val impl: InputNode
@ExperimentalFrpApi
suspend fun emit(value: T) {
coroutineScope {
+ var jobOrNull: Job? = null
val newEmit =
async(start = CoroutineStart.LAZY) {
- network.transaction { impl.visit(this, value) }.await()
+ jobOrNull?.join()
+ network
+ .transaction("MutableTFlow($name).emit") { impl.visit(this, value) }
+ .await()
}
- val jobOrNull = storage.getAndSet(newEmit)
- if (jobOrNull?.isActive != true) {
- newEmit.await()
- } else {
- jobOrNull.join()
- }
+ jobOrNull = storage.getAndSet(newEmit)
+ newEmit.await()
}
}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
index a4c695657f8d..80e74748a375 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
@@ -121,7 +121,7 @@ fun <A, B, C> TState<A>.combineWith(
/**
* Splits a [TState] of pairs into a pair of [TFlows][TState], where each returned [TState] holds
- * hald of the original.
+ * half of the original.
*
* Shorthand for:
* ```kotlin
@@ -312,6 +312,57 @@ fun <A, B, C, D, Z> combine(
)
}
+/**
+ * Returns a [TState] whose value is generated with [transform] by combining the current values of
+ * each given [TState].
+ *
+ * @see TState.combineWith
+ */
+@ExperimentalFrpApi
+fun <A, B, C, D, E, Z> combine(
+ stateA: TState<A>,
+ stateB: TState<B>,
+ stateC: TState<C>,
+ stateD: TState<D>,
+ stateE: TState<E>,
+ transform: suspend FrpScope.(A, B, C, D, E) -> Z,
+): TState<Z> {
+ val operatorName = "combine"
+ val name = operatorName
+ return TStateInit(
+ init(name) {
+ coroutineScope {
+ val dl1: Deferred<TStateImpl<A>> = async {
+ stateA.init.connect(evalScope = this@init)
+ }
+ val dl2: Deferred<TStateImpl<B>> = async {
+ stateB.init.connect(evalScope = this@init)
+ }
+ val dl3: Deferred<TStateImpl<C>> = async {
+ stateC.init.connect(evalScope = this@init)
+ }
+ val dl4: Deferred<TStateImpl<D>> = async {
+ stateD.init.connect(evalScope = this@init)
+ }
+ val dl5: Deferred<TStateImpl<E>> = async {
+ stateE.init.connect(evalScope = this@init)
+ }
+ zipStates(
+ name,
+ operatorName,
+ dl1.await(),
+ dl2.await(),
+ dl3.await(),
+ dl4.await(),
+ dl5.await(),
+ ) { a, b, c, d, e ->
+ NoScope.runInFrpScope { transform(a, b, c, d, e) }
+ }
+ }
+ }
+ )
+}
+
/** Returns a [TState] by applying [transform] to the value held by the original [TState]. */
@ExperimentalFrpApi
fun <A, B> TState<A>.flatMap(transform: suspend FrpScope.(A) -> TState<B>): TState<B> {
@@ -367,7 +418,7 @@ fun <A> TState<A>.selector(numDistinctValues: Int? = null): TStateSelector<A> =
* @see selector
*/
@ExperimentalFrpApi
-class TStateSelector<A>
+class TStateSelector<in A>
internal constructor(
private val upstream: TState<A>,
private val groupedChanges: GroupedTFlow<A, Boolean>,
@@ -406,6 +457,7 @@ internal constructor(internal val network: Network, initialValue: Deferred<T>) :
private val input: CoalescingMutableTFlow<Deferred<T>, Deferred<T>?> =
CoalescingMutableTFlow(
+ name = null,
coalesce = { _, new -> new },
network = network,
getInitialValue = { null },
@@ -423,7 +475,7 @@ internal constructor(internal val network: Network, initialValue: Deferred<T>) :
.cached()
state = TStateSource(name, operatorName, initialValue, calm)
@Suppress("DeferredResultUnused")
- network.transaction {
+ network.transaction("MutableTState.init") {
calm.activate(evalScope = this, downstream = Schedulable.S(state))?.let {
(connection, needsEval) ->
state.upstreamConnection = connection
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
index 4f302a14ff00..0674a2e75659 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
@@ -31,6 +31,8 @@ import com.android.systemui.kairos.internal.TStateSource
import com.android.systemui.kairos.util.Just
import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.flatMap
+import com.android.systemui.kairos.util.map
import com.android.systemui.kairos.util.none
import com.android.systemui.kairos.util.orElseGet
@@ -178,3 +180,24 @@ private fun <A> TStateImpl<A>.getUnsafe(): Maybe<A> =
is TStateSource -> getStorageUnsafe()
is DerivedMapCheap<*, *> -> none
}
+
+private fun <A> TStateImpl<A>.getUnsafeWithEpoch(): Maybe<Pair<A, Long>> =
+ when (this) {
+ is TStateDerived -> getCachedUnsafe().map { it to invalidatedEpoch }
+ is TStateSource -> getStorageUnsafe().map { it to writeEpoch }
+ is DerivedMapCheap<*, *> -> none
+ }
+
+/**
+ * Returns the current value held in this [TState], or [none] if the [TState] has not been
+ * initialized.
+ *
+ * The returned [Long] is the *epoch* at which the internal cache was last updated. This can be used
+ * to identify values which are out-of-date.
+ */
+fun <A> TState<A>.sampleUnsafe(): Maybe<Pair<A, Long>> =
+ when (this) {
+ is MutableTState -> tState.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
+ is TStateInit -> init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
+ is TStateLoop -> this.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
+ }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
index 90f1aea3e42f..7e6384925f38 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
@@ -34,9 +34,9 @@ import com.android.systemui.kairos.TFlowInit
import com.android.systemui.kairos.groupByKey
import com.android.systemui.kairos.init
import com.android.systemui.kairos.internal.util.childScope
-import com.android.systemui.kairos.internal.util.launchOnCancel
import com.android.systemui.kairos.internal.util.mapValuesParallel
import com.android.systemui.kairos.launchEffect
+import com.android.systemui.kairos.mergeLeft
import com.android.systemui.kairos.util.Just
import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.None
@@ -49,7 +49,6 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CompletableJob
-import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
@@ -86,8 +85,7 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
builder: suspend S.() -> Unit,
): TFlow<A> {
var job: Job? = null
- val stopEmitter = newStopEmitter()
- val handle = this.job.invokeOnCompletion { stopEmitter.emit(Unit) }
+ val stopEmitter = newStopEmitter("buildTFlow")
// Create a child scope that will be kept alive beyond the end of this transaction.
val childScope = coroutineScope.childScope()
lateinit var emitter: Pair<T, S>
@@ -99,7 +97,6 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
reenterBuildScope(this@BuildScopeImpl, childScope).runInBuildScope {
launchEffect {
builder(emitter.second)
- handle.dispose()
stopEmitter.emit(Unit)
}
}
@@ -110,7 +107,7 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
},
)
emitter = constructFlow(inputNode)
- return with(frpScope) { emitter.first.takeUntil(stopEmitter) }
+ return with(frpScope) { emitter.first.takeUntil(mergeLeft(stopEmitter, endSignal)) }
}
private fun <T> tFlowInternal(builder: suspend FrpProducerScope<T>.() -> Unit): TFlow<T> =
@@ -134,7 +131,8 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
): TFlow<Out> =
buildTFlow(
constructFlow = { inputNode ->
- val flow = CoalescingMutableTFlow(coalesce, network, getInitialValue, inputNode)
+ val flow =
+ CoalescingMutableTFlow(null, coalesce, network, getInitialValue, inputNode)
flow to
object : FrpCoalescingProducerScope<In> {
override fun emit(value: In) {
@@ -164,11 +162,13 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
val subRef = AtomicReference<Maybe<Output<A>>>(null)
val childScope = coroutineScope.childScope()
// When our scope is cancelled, deactivate this observer.
- childScope.launchOnCancel(CoroutineName("TFlow.observeEffect")) {
+ childScope.coroutineContext.job.invokeOnCompletion {
subRef.getAndSet(None)?.let { output ->
if (output is Just) {
@Suppress("DeferredResultUnused")
- network.transaction { scheduleDeactivation(output.value) }
+ network.transaction("observeEffect cancelled") {
+ scheduleDeactivation(output.value)
+ }
}
}
}
@@ -215,7 +215,7 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
} else if (needsEval) {
outputNode.schedule(evalScope = stateScope.evalScope)
}
- } ?: childScope.cancel()
+ } ?: run { childScope.cancel() }
}
return childScope.coroutineContext.job
}
@@ -229,10 +229,7 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
"mapBuild",
mapImpl({ init.connect(evalScope = this) }) { spec ->
reenterBuildScope(outerScope = this@BuildScopeImpl, childScope)
- .runInBuildScope {
- val (result, _) = asyncScope { transform(spec) }
- result.get()
- }
+ .runInBuildScope { transform(spec) }
}
.cached(),
)
@@ -272,8 +269,9 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
return changes to FrpDeferredValue(initOut)
}
- private fun newStopEmitter(): CoalescingMutableTFlow<Unit, Unit> =
+ private fun newStopEmitter(name: String): CoalescingMutableTFlow<Unit, Unit> =
CoalescingMutableTFlow(
+ name = name,
coalesce = { _, _: Unit -> },
network = network,
getInitialValue = {},
@@ -299,17 +297,19 @@ internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope
}
private fun mutableChildBuildScope(): BuildScopeImpl {
- val stopEmitter = newStopEmitter()
+ val stopEmitter = newStopEmitter("mutableChildBuildScope")
val childScope = coroutineScope.childScope()
childScope.coroutineContext.job.invokeOnCompletion { stopEmitter.emit(Unit) }
// Ensure that once this transaction is done, the new child scope enters the completing
// state (kept alive so long as there are child jobs).
- scheduleOutput(
- OneShot {
- // TODO: don't like this cast
- (childScope.coroutineContext.job as CompletableJob).complete()
- }
- )
+ // TODO: need to keep the scope alive if it's used to accumulate state.
+ // Otherwise, stopEmitter will emit early, due to the call to complete().
+ // scheduleOutput(
+ // OneShot {
+ // // TODO: don't like this cast
+ // (childScope.coroutineContext.job as CompletableJob).complete()
+ // }
+ // )
return BuildScopeImpl(
stateScope = StateScopeImpl(evalScope = stateScope.evalScope, endSignal = stopEmitter),
coroutineScope = childScope,
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 3aec319881d0..04ce5b6d8785 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
@@ -86,7 +86,7 @@ internal class DepthTracker {
@Volatile private var dirty_depthIsDirect = true
@Volatile private var dirty_isIndirectRoot = false
- suspend fun schedule(scheduler: Scheduler, node: MuxNode<*, *, *>) {
+ fun schedule(scheduler: Scheduler, node: MuxNode<*, *, *>) {
if (dirty_depthIsDirect) {
scheduler.schedule(dirty_directDepth, node)
} else {
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
index af864e6c3496..69994ba6e866 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
@@ -66,8 +66,6 @@ internal interface NetworkScope : InitScope {
fun schedule(state: TStateSource<*>)
- suspend fun schedule(node: MuxNode<*, *, *>)
-
fun scheduleDeactivation(node: PushNode<*>)
fun scheduleDeactivation(output: Output<*>)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
index f7ff15f0507b..af68a1e3d83c 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
@@ -188,6 +188,14 @@ internal sealed class MuxNode<K : Any, V, Output>(val lifecycle: MuxLifecycle<Ou
}
abstract fun hasCurrentValueLocked(transactionStore: TransactionStore): Boolean
+
+ fun schedule(evalScope: EvalScope) {
+ // TODO: Potential optimization
+ // Detect if this node is guaranteed to have a single upstream within this transaction,
+ // then bypass scheduling it. Instead immediately schedule its downstream and treat this
+ // MuxNode as a Pull (effectively making it a mapCheap).
+ depthTracker.schedule(evalScope.scheduler, this)
+ }
}
/** An input branch of a mux node, associated with a key. */
@@ -202,7 +210,7 @@ internal class MuxBranchNode<K : Any, V>(private val muxNode: MuxNode<K, V, *>,
val upstreamResult = upstream.getPushEvent(evalScope)
if (upstreamResult is Just) {
muxNode.upstreamData[key] = upstreamResult.value
- evalScope.schedule(muxNode)
+ muxNode.schedule(evalScope)
}
}
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 08bee855831a..3b9502a5d812 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
@@ -409,7 +409,7 @@ internal fun <K : Any, A> switchDeferredImpl(
// Schedule for evaluation if any switched-in nodes have already emitted within
// this transaction.
if (muxNode.upstreamData.isNotEmpty()) {
- evalScope.schedule(muxNode)
+ muxNode.schedule(evalScope)
}
return muxNode.takeUnless { muxNode.switchedIn.isEmpty() && !isIndirect }
}
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 cdfafa943121..b291c879b449 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
@@ -75,7 +75,7 @@ internal class MuxPromptMovingNode<K : Any, V>(
if (depthTracker.dirty_depthIncreased()) {
depthTracker.schedule(evalScope.compactor, node = this)
}
- evalScope.schedule(this)
+ schedule(evalScope)
} else {
val compactDownstream = depthTracker.isDirty()
if (evalResult != null || compactDownstream) {
@@ -291,7 +291,7 @@ internal class MuxPromptPatchNode<K : Any, V>(private val muxNode: MuxPromptMovi
val upstreamResult = upstream.getPushEvent(evalScope)
if (upstreamResult is Just) {
muxNode.patchData = upstreamResult.value
- evalScope.schedule(muxNode)
+ muxNode.schedule(evalScope)
}
}
@@ -451,7 +451,7 @@ internal fun <K : Any, A> switchPromptImpl(
// Schedule for evaluation if any switched-in nodes or the patches node have
// already emitted within this transaction.
if (movingNode.patchData != null || movingNode.upstreamData.isNotEmpty()) {
- evalScope.schedule(movingNode)
+ movingNode.schedule(evalScope)
}
return movingNode.takeUnless { it.patches == null && it.switchedIn.isEmpty() }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
index f0df89d780c9..599b18695034 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
@@ -81,11 +81,6 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
stateWrites.add(state)
}
- // TODO: weird that we have this *and* scheduler exposed
- override suspend fun schedule(node: MuxNode<*, *, *>) {
- scheduler.schedule(node.depthTracker.dirty_directDepth, node)
- }
-
override fun scheduleDeactivation(node: PushNode<*>) {
deactivations.add(node)
}
@@ -95,9 +90,7 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
}
/** Listens for external events and starts FRP transactions. Runs forever. */
- suspend fun runInputScheduler() = coroutineScope {
- launch { scheduler.activate() }
- launch { compactor.activate() }
+ suspend fun runInputScheduler() {
val actions = mutableListOf<ScheduledAction<*>>()
for (first in inputScheduleChan) {
// Drain and conflate all transaction requests into a single transaction
@@ -125,12 +118,12 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
}
/** Evaluates [block] inside of a new transaction when the network is ready. */
- fun <R> transaction(block: suspend EvalScope.() -> R): Deferred<R> =
+ fun <R> transaction(reason: String, block: suspend EvalScope.() -> R): Deferred<R> =
CompletableDeferred<R>(parent = coroutineScope.coroutineContext.job).also { onResult ->
val job =
coroutineScope.launch {
inputScheduleChan.send(
- ScheduledAction(onStartTransaction = block, onResult = onResult)
+ ScheduledAction(reason, onStartTransaction = block, onResult = onResult)
)
}
onResult.invokeOnCompletion { job.cancel() }
@@ -229,6 +222,7 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
}
internal class ScheduledAction<T>(
+ val reason: String,
private val onResult: CompletableDeferred<T>? = null,
private val onStartTransaction: suspend EvalScope.() -> T,
) {
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
index 872fb7a6cb74..c12ef6ae6a5d 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
@@ -21,44 +21,34 @@ package com.android.systemui.kairos.internal
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.PriorityBlockingQueue
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
internal interface Scheduler {
- suspend fun schedule(depth: Int, node: MuxNode<*, *, *>)
+ fun schedule(depth: Int, node: MuxNode<*, *, *>)
- suspend fun scheduleIndirect(indirectDepth: Int, node: MuxNode<*, *, *>)
+ fun scheduleIndirect(indirectDepth: Int, node: MuxNode<*, *, *>)
}
internal class SchedulerImpl : Scheduler {
val enqueued = ConcurrentHashMap<MuxNode<*, *, *>, Any>()
val scheduledQ = PriorityBlockingQueue<Pair<Int, MuxNode<*, *, *>>>(16, compareBy { it.first })
- val chan = Channel<Pair<Int, MuxNode<*, *, *>>>(Channel.UNLIMITED)
- override suspend fun schedule(depth: Int, node: MuxNode<*, *, *>) {
+ override fun schedule(depth: Int, node: MuxNode<*, *, *>) {
if (enqueued.putIfAbsent(node, node) == null) {
- chan.send(Pair(depth, node))
+ scheduledQ.add(Pair(depth, node))
}
}
- override suspend fun scheduleIndirect(indirectDepth: Int, node: MuxNode<*, *, *>) {
+ override fun scheduleIndirect(indirectDepth: Int, node: MuxNode<*, *, *>) {
schedule(Int.MIN_VALUE + indirectDepth, node)
}
- suspend fun activate() {
- for (nodeSchedule in chan) {
- scheduledQ.add(nodeSchedule)
- drainChan()
- }
- }
-
internal suspend fun drainEval(network: Network) {
drain { runStep ->
runStep { muxNode -> network.evalScope { muxNode.visit(this) } }
// If any visited MuxPromptNodes had their depths increased, eagerly propagate those
- // depth
- // changes now before performing further network evaluation.
+ // depth changes now before performing further network evaluation.
network.compactor.drainCompact()
}
}
@@ -71,19 +61,12 @@ internal class SchedulerImpl : Scheduler {
crossinline onStep:
suspend (runStep: suspend (visit: suspend (MuxNode<*, *, *>) -> Unit) -> Unit) -> Unit
): Unit = coroutineScope {
- while (!chan.isEmpty || scheduledQ.isNotEmpty()) {
- drainChan()
+ while (scheduledQ.isNotEmpty()) {
val maxDepth = scheduledQ.peek()?.first ?: error("Unexpected empty scheduler")
onStep { visit -> runStep(maxDepth, visit) }
}
}
- private suspend fun drainChan() {
- while (!chan.isEmpty) {
- scheduledQ.add(chan.receive())
- }
- }
-
private suspend inline fun runStep(
maxDepth: Int,
crossinline visit: suspend (MuxNode<*, *, *>) -> Unit,
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
index 5cec05c8ef2d..c68b4c366776 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
@@ -314,6 +314,28 @@ internal fun <A, B, C, D, Z> zipStates(
@Suppress("UNCHECKED_CAST") transform(a as A, b as B, c as C, d as D)
}
+internal fun <A, B, C, D, E, Z> zipStates(
+ name: String?,
+ operatorName: String,
+ l1: TStateImpl<A>,
+ l2: TStateImpl<B>,
+ l3: TStateImpl<C>,
+ l4: TStateImpl<D>,
+ l5: TStateImpl<E>,
+ transform: suspend EvalScope.(A, B, C, D, E) -> Z,
+): TStateImpl<Z> =
+ zipStates(null, operatorName, mapOf(0 to l1, 1 to l2, 2 to l3, 3 to l4, 4 to l5)).map(
+ name,
+ operatorName,
+ ) {
+ val a = it.getValue(0)
+ val b = it.getValue(1)
+ val c = it.getValue(2)
+ val d = it.getValue(3)
+ val e = it.getValue(4)
+ @Suppress("UNCHECKED_CAST") transform(a as A, b as B, c as C, d as D, e as E)
+ }
+
internal fun <K : Any, A> zipStates(
name: String?,
operatorName: String,
diff --git a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
index 165230b2aeaf..688adae8fcae 100644
--- a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
@@ -1170,12 +1170,12 @@ class KairosTests {
mergeIncrementally
.onEach { println("patch: $it") }
.foldMapIncrementally()
- .flatMap { it.combineValues() }
+ .flatMap { it.combine() }
}
}
}
.foldMapIncrementally()
- .flatMap { it.combineValues() }
+ .flatMap { it.combine() }
accState.toStateFlow()
}
@@ -1300,6 +1300,26 @@ class KairosTests {
}
@Test
+ fun buildScope_stateAccumulation() = runFrpTest { network ->
+ val input = network.mutableTFlow<Unit>()
+ var observedCount: Int? = null
+ activateSpec(network) {
+ val (c, j) = asyncScope { input.fold(0) { _, x -> x + 1 } }
+ deferredBuildScopeAction { c.get().observe { observedCount = it } }
+ }
+ runCurrent()
+ assertEquals(0, observedCount)
+
+ input.emit(Unit)
+ runCurrent()
+ assertEquals(1, observedCount)
+
+ input.emit(Unit)
+ runCurrent()
+ assertEquals(2, observedCount)
+ }
+
+ @Test
fun effect() = runFrpTest { network ->
val input = network.mutableTFlow<Unit>()
var effectRunning = false
diff --git a/packages/Vcn/flags/Android.bp b/packages/Vcn/flags/Android.bp
index 3943c6f7fe24..8d09fdbfa3de 100644
--- a/packages/Vcn/flags/Android.bp
+++ b/packages/Vcn/flags/Android.bp
@@ -29,10 +29,24 @@ aconfig_declarations {
],
}
+// TODO: b/374174952 Remove this library when VCN modularization is done
java_aconfig_library {
name: "android.net.vcn.flags-aconfig-java-export",
aconfig_declarations: "android.net.vcn.flags-aconfig",
mode: "exported",
min_sdk_version: "35",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
+
+java_aconfig_library {
+ name: "android.net.vcn.flags-aconfig-java",
+ aconfig_declarations: "android.net.vcn.flags-aconfig",
+ min_sdk_version: "35",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ apex_available: [
+ "com.android.tethering",
+ ],
}
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index c3121162b7f2..edb22c0e7aa0 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -32,9 +32,9 @@ filegroup {
}
java_defaults {
- name: "framework-connectivity-b-defaults",
+ name: "framework-connectivity-b-defaults-base",
sdk_version: "module_current",
- min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+
defaults: ["framework-module-defaults"], // This is a boot jar
srcs: [
@@ -44,14 +44,10 @@ java_defaults {
libs: [
"android.net.ipsec.ike.stubs.module_lib",
- "app-compat-annotations",
"framework-wifi.stubs.module_lib",
"unsupportedappusage",
],
- static_libs: [
- //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
- "android.net.vcn.flags-aconfig-java-export",
- ],
+
aidl: {
include_dirs: [
// For connectivity-framework classes such as Network.aidl, NetworkCapabilities.aidl
@@ -60,16 +56,83 @@ java_defaults {
},
}
+soong_config_module_type {
+ name: "framework_connectivity_b_defaults_soong_config",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "is_vcn_in_mainline",
+ ],
+ properties: [
+ "min_sdk_version",
+ "static_libs",
+ "apex_available",
+ ],
+}
+
+framework_connectivity_b_defaults_soong_config {
+ name: "framework-connectivity-b-defaults",
+ defaults: [
+ "framework-connectivity-b-defaults-base",
+ ],
+ soong_config_variables: {
+ is_vcn_in_mainline: {
+ //TODO: b/380155299 Make it Baklava when aidl tool can understand it
+ min_sdk_version: "current",
+ static_libs: ["android.net.vcn.flags-aconfig-java"],
+ apex_available: ["com.android.tethering"],
+
+ conditions_default: {
+ min_sdk_version: "35",
+ static_libs: ["android.net.vcn.flags-aconfig-java-export"],
+ apex_available: ["//apex_available:platform"],
+ },
+ },
+ },
+}
+
+soong_config_module_type {
+ name: "framework_connectivity_b_java_sdk_library_defaults_soong_config",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "is_vcn_in_mainline",
+ ],
+ properties: [
+ "aconfig_declarations",
+ "jarjar_rules",
+ ],
+}
+
+framework_connectivity_b_java_sdk_library_defaults_soong_config {
+ name: "framework-connectivity-b-java-sdk-library-defaults",
+ soong_config_variables: {
+ is_vcn_in_mainline: {
+ aconfig_declarations: ["android.net.vcn.flags-aconfig-java"],
+
+ // TODO: b/375213246 Use the connectivity jarjar rule generator to create the
+ // jarjar rules. In the end state, use "framework-connectivity-jarjar-rules"
+ // after VCN code is moved to the Connectivity folder
+ jarjar_rules: "framework-vcn-jarjar-rules.txt",
+
+ conditions_default: {
+ aconfig_declarations: ["android.net.vcn.flags-aconfig-java-export"],
+
+ // Use "android.net.connectivity" as prefix would trigger
+ // "Hidden API flags are inconsistent" build error
+ jarjar_rules: "framework-vcn-jarjar-rules-platform.txt",
+ },
+ },
+ },
+}
+
java_sdk_library {
name: "framework-connectivity-b",
defaults: [
"framework-connectivity-b-defaults",
+ "framework-connectivity-b-java-sdk-library-defaults",
],
- //TODO: b/375213246 Use "framework-connectivity-jarjar-rules" when VCN is
- // in mainline
- jarjar_rules: "framework-vcn-jarjar-rules.txt",
-
permitted_packages: [
"android.net",
"android.net.vcn",
@@ -92,11 +155,6 @@ java_sdk_library {
"framework-connectivity-pre-jarjar",
],
- aconfig_declarations: [
- //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
- "android.net.vcn.flags-aconfig-java-export",
- ],
-
impl_library_visibility: [
// Using for test only
"//cts/tests/netlegacy22.api",
@@ -120,17 +178,13 @@ java_sdk_library {
"//packages/modules/Wifi/service/tests/wifitests",
],
- apex_available: [
- // TODO: b/374174952 Remove it when VCN modularization is released
- "//apex_available:platform",
-
- "com.android.tethering",
- ],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
java_library {
name: "framework-connectivity-b-pre-jarjar",
defaults: ["framework-connectivity-b-defaults"],
+ installable: false,
libs: [
"framework-connectivity-pre-jarjar",
],
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt
new file mode 100644
index 000000000000..757043bdbbc0
--- /dev/null
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt
@@ -0,0 +1,2 @@
+rule android.net.vcn.persistablebundleutils.** android.net.vcn.module.repackaged.persistablebundleutils.@1
+rule android.net.vcn.util.** android.net.vcn.module.repackaged.util.@1 \ No newline at end of file
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
index 757043bdbbc0..7e27b24f749c 100644
--- a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
@@ -1,2 +1,2 @@
-rule android.net.vcn.persistablebundleutils.** android.net.vcn.module.repackaged.persistablebundleutils.@1
-rule android.net.vcn.util.** android.net.vcn.module.repackaged.util.@1 \ No newline at end of file
+rule android.net.vcn.persistablebundleutils.** android.net.connectivity.android.net.vcn.persistablebundleutils.@1
+rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1 \ No newline at end of file
diff --git a/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java b/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
index 1f0fa92d7976..de22ca684871 100644
--- a/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
+++ b/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
@@ -23,8 +23,6 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.compat.Compatibility;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.vcn.IVcnManagementService;
@@ -40,17 +38,15 @@ import android.os.SystemProperties;
@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class ConnectivityFrameworkInitializerBaklava {
- /**
- * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link
- * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers
- * that depend on them. If the feature is missing, {@link Context#getSystemService} will return
- * null.
- *
- * <p>This change is specific to VcnManager.
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- private static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
+
+ // This is a copy of TelephonyFrameworkInitializer.ENABLE_CHECKING_TELEPHONY_FEATURES. This
+ // ChangeId will replace ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN to gate VcnManager
+ // feature flag enforcement.
+ // This replacement is safe because both ChangeIds have been enabled since Android V and serve
+ // the same purpose: enforcing telephony feature flag checks before using telephony-based
+ // features. This also simplifies VCN modularization by avoiding the need to handle different
+ // states, such as: SDK < B vs. SDK >= B; VCN in platform vs. VCN in the apex.
+ private static final long ENABLE_CHECKING_TELEPHONY_FEATURES = 330583731;
/**
* The corresponding vendor API for Android V
@@ -71,7 +67,7 @@ public final class ConnectivityFrameworkInitializerBaklava {
private static String getVcnFeatureDependency() {
// Check SDK version of the client app. Apps targeting pre-V SDK might
// have not checked for existence of these features.
- if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN)) {
+ if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES)) {
return null;
}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index c1a1ee7875d0..1370b0678cc5 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -32,11 +32,9 @@ filegroup {
visibility: ["//frameworks/base/services/core"],
}
-// Do not static include this lib in VCN because these files exist in
-// both service-connectivity.jar and framework.jar
-// TODO: b/374174952 After VCN moves to Connectivity/ and the modularization is done
-// this lib can be removed and "service-connectivity-b-pre-jarjar" can include
-// "service-connectivity-pre-jarjar"
+// TODO: b/374174952 This library is only used in "service-connectivity-b-platform"
+// After VCN moves to Connectivity/ and the modularization is done, this lib and
+// "service-connectivity-b-platform" can both be removed
java_library {
name: "connectivity-utils-service-vcn-internal",
sdk_version: "module_current",
@@ -48,30 +46,30 @@ java_library {
"framework-annotations-lib",
"unsupportedappusage",
],
- visibility: [
- "//visibility:private",
- ],
- apex_available: [
- // TODO: b/374174952 Remove it when VCN modularization is released
- "//apex_available:platform",
+ visibility: ["//visibility:private"],
+}
- "com.android.tethering",
+filegroup {
+ name: "service-vcn-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+ visibility: [
+ "//packages/modules/Connectivity/service-b",
],
}
-java_library {
- name: "service-connectivity-b-pre-jarjar",
- sdk_version: "system_server_current",
- min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+// This java_defaults will be used for "service-connectivity-b-platform" and
+// "service-connectivity-b-pre-jarjar"
+java_defaults {
+ name: "service-connectivity-b-pre-jarjar-defaults",
defaults: ["framework-system-server-module-defaults"], // This is a system server jar
srcs: [
- "src/**/*.java",
+ ":service-vcn-sources",
],
libs: [
"android.net.ipsec.ike.stubs.module_lib",
- "connectivity-utils-service-vcn-internal",
"framework-annotations-lib",
"framework-connectivity-pre-jarjar",
"framework-connectivity-t-pre-jarjar",
@@ -89,13 +87,30 @@ java_library {
"modules-utils-handlerexecutor",
],
+ defaults_visibility: [
+ "//packages/modules/Connectivity/service-b",
+ ],
+}
+
+// This library is only used to be included into services.jar when the build system
+// flag RELEASE_MOVE_VCN_TO_MAINLINE is disabled. When the flag is enabled, a module
+// version of this library will be included in Tethering module
+java_library {
+ name: "service-connectivity-b-platform",
+ defaults: ["service-connectivity-b-pre-jarjar-defaults"],
+ static_libs: ["connectivity-utils-service-vcn-internal"],
+
+ sdk_version: "system_server_current",
+ min_sdk_version: "35",
+
+ // TODO (b/374174952 ): This file is for jarjaring files in
+ // "connectivity-utils-service-vcn-internal".
+ jarjar_rules: "service-vcn-platform-jarjar-rules.txt",
+
visibility: [
"//frameworks/base/services",
],
apex_available: [
- // TODO: b/374174952 Remove it when VCN modularization is released
"//apex_available:platform",
-
- "com.android.tethering",
],
}
diff --git a/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt b/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt
new file mode 100644
index 000000000000..36307277b4b9
--- /dev/null
+++ b/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt
@@ -0,0 +1,5 @@
+rule android.util.IndentingPrintWriter android.net.vcn.module.repackaged.android.util.IndentingPrintWriter
+rule android.util.LocalLog android.net.vcn.module.repackaged.android.util.LocalLog
+rule com.android.internal.util.IndentingPrintWriter android.net.vcn.module.repackaged.com.android.internal.util.IndentingPrintWriter
+rule com.android.internal.util.MessageUtils android.net.vcn.module.repackaged.com.android.internal.util.MessageUtils
+rule com.android.internal.util.WakeupMessage android.net.vcn.module.repackaged.com.android.internal.util.WakeupMessage \ No newline at end of file
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
index 02c8ce4f29e9..81c7edf4adf1 100644
--- a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -16,7 +16,9 @@
package com.android.server;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
@@ -30,6 +32,8 @@ import com.android.tools.r8.keepanno.annotations.UsedByReflection;
// Without this annotation, this class will be treated as unused class and be removed during build
// time.
@UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public final class ConnectivityServiceInitializerB extends SystemService {
private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
private final VcnManagementService mVcnManagementService;
diff --git a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
index 26db6a988e31..c9a99d729e91 100644
--- a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
+++ b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
@@ -37,6 +37,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -164,6 +165,8 @@ import java.util.concurrent.TimeUnit;
* @hide
*/
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
@NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
@@ -297,8 +300,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
});
}
- // Package-visibility for SystemServer to create instances.
- static VcnManagementService create(@NonNull Context context) {
+ /** Called by ConnectivityServiceInitializerB to create instances. */
+ // VcnManagementService will be jarjared but ConnectivityServiceInitializerB will not. Thus this
+ // method needs to be public for ConnectivityServiceInitializerB to access
+ public static VcnManagementService create(@NonNull Context context) {
return new VcnManagementService(context, new Dependencies());
}
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
index b448f7595b3b..b04e25dff276 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -22,12 +22,14 @@ import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.vcn.VcnManager;
import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -77,6 +79,8 @@ import java.util.Set;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class TelephonySubscriptionTracker extends BroadcastReceiver {
@NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
private static final boolean LOG_DBG = false; // STOPSHIP if true
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 2524d0eedac3..369ef6ae6a3f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -29,6 +29,7 @@ import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.NetworkCapabilities;
@@ -39,6 +40,7 @@ import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.util.LogUtils;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -75,6 +77,8 @@ import java.util.Set;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index f024b5f05bf6..300b80f942ef 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -37,6 +37,8 @@ import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
@@ -82,6 +84,7 @@ import android.net.vcn.util.LogUtils;
import android.net.vcn.util.MtuUtils;
import android.net.vcn.util.OneWayBoolean;
import android.net.wifi.WifiInfo;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -171,6 +174,8 @@ import java.util.function.Consumer;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
@@ -1228,10 +1233,7 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setSafeModeAlarm() {
- final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig();
- logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled);
-
- if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) {
+ if (!mConnectionConfig.isSafeModeEnabled()) {
logVdbg("setSafeModeAlarm: safe mode disabled");
return;
}
@@ -2945,6 +2947,10 @@ public class VcnGatewayConnection extends StateMachine {
*
* <p>Synchronize this action to minimize locking around WakeLock use.
*/
+ // WakelockTimeout suppressed because the time the wake lock is needed for is unknown. The
+ // wakelock is only acquired when a Message is sent to this state machine and will be
+ // released when the message is processed or the state machin quits
+ @SuppressLint("WakelockTimeout")
public synchronized void acquire() {
mImpl.acquire();
}
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 4552f509b59a..99c848f53c39 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -25,6 +25,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
+import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
@@ -32,6 +33,7 @@ import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.ArraySet;
@@ -54,6 +56,8 @@ import java.util.concurrent.Executor;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class VcnNetworkProvider extends NetworkProvider {
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 72de61363d26..6467af4355f6 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -23,6 +23,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +32,7 @@ import android.net.ConnectivityManager;
import android.net.IpSecTransformState;
import android.net.Network;
import android.net.vcn.VcnManager;
+import android.os.Build;
import android.os.Handler;
import android.os.OutcomeReceiver;
import android.os.PowerManager;
@@ -59,6 +61,8 @@ import java.util.concurrent.TimeUnit;
*
* <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state"
*/
+// TODO(b/374174952) Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class IpSecPacketLossDetector extends NetworkMetricMonitor {
private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index 86cee554be6f..14853440a129 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -22,9 +22,11 @@ import static com.android.server.VcnManagementService.LOCAL_LOG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.net.IpSecTransform;
import android.net.IpSecTransformState;
import android.net.Network;
+import android.os.Build;
import android.os.OutcomeReceiver;
import android.util.CloseGuard;
import android.util.Slog;
@@ -42,6 +44,8 @@ import java.util.concurrent.Executor;
*
* <p>This class is flag gated by "network_metric_monitor"
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public abstract class NetworkMetricMonitor implements AutoCloseable {
private static final String TAG = NetworkMetricMonitor.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 79c4116d0cd4..705141f3f1b4 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -29,12 +29,14 @@ import static com.android.server.VcnManagementService.LOCAL_LOG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
+import android.os.Build;
import android.os.ParcelUuid;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -50,6 +52,8 @@ import java.util.Map;
import java.util.Set;
/** @hide */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
class NetworkPriorityClassifier {
@NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
/**
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 29a0762f5fe8..bc552e7e6afd 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -28,6 +28,7 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.ge
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IpSecTransform;
@@ -40,6 +41,7 @@ import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.util.LogUtils;
+import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.telephony.TelephonyCallback;
@@ -73,6 +75,8 @@ import java.util.TreeSet;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class UnderlyingNetworkController {
@NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 30f4ed1b9f0b..776931bad73b 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -22,12 +22,14 @@ import static com.android.server.VcnManagementService.LOCAL_LOG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.IndentingPrintWriter;
@@ -50,6 +52,8 @@ import java.util.concurrent.TimeUnit;
*
* @hide
*/
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class UnderlyingNetworkEvaluator {
private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
diff --git a/proto/Android.bp b/proto/Android.bp
index a5e13350ebd2..feaa6d2e9b73 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -25,6 +25,10 @@ java_library_static {
static_libs: ["libprotobuf-java-nano"],
},
},
+ apex_available: [
+ "com.android.neuralnetworks",
+ "//apex_available:platform",
+ ],
}
java_library_static {
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 66c8d0fa32f9..59043a8356ae 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -182,21 +182,6 @@ java_device_for_host {
visibility: [":__subpackages__"],
}
-// Separated out from ravenwood-junit-impl since it needs to compile
-// against `module_current`
-java_library {
- name: "ravenwood-junit-impl-flag",
- srcs: [
- "junit-flag-src/**/*.java",
- ],
- sdk_version: "module_current",
- libs: [
- "junit",
- "flag-junit",
- ],
- visibility: ["//visibility:public"],
-}
-
// Carefully compiles against only module_current to support tests that
// want to verify they're unbundled. The "impl" library above is what
// ships inside the Ravenwood environment to actually drive any API
@@ -651,7 +636,6 @@ android_ravenwood_libgroup {
"flag-junit",
"ravenwood-framework",
"ravenwood-junit-impl",
- "ravenwood-junit-impl-flag",
"mockito-ravenwood-prebuilt",
"inline-mockito-ravenwood-prebuilt",
diff --git a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
deleted file mode 100644
index 9d6277473298..000000000000
--- a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
+++ /dev/null
@@ -1,54 +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 android.platform.test.flag.junit;
-
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.IFlagsValueProvider;
-
-/**
- * Offer to create {@link CheckFlagsRule} instances that are useful on the Ravenwood deviceless
- * testing environment.
- *
- * At the moment, default flag values are not available on Ravenwood, so the only options offered
- * here are "all-on" and "all-off" options. Tests that want to exercise specific flag states should
- * use {@link android.platform.test.flag.junit.SetFlagsRule}.
- */
-public class RavenwoodFlagsValueProvider {
- /**
- * Create a {@link CheckFlagsRule} instance where flags are in an "all-on" state.
- */
- public static CheckFlagsRule createAllOnCheckFlagsRule() {
- return new CheckFlagsRule(new IFlagsValueProvider() {
- @Override
- public boolean getBoolean(String flag) {
- return true;
- }
- });
- }
-
- /**
- * Create a {@link CheckFlagsRule} instance where flags are in an "all-off" state.
- */
- public static CheckFlagsRule createAllOffCheckFlagsRule() {
- return new CheckFlagsRule(new IFlagsValueProvider() {
- @Override
- public boolean getBoolean(String flag) {
- return false;
- }
- });
- }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 9644a52a749e..3ebef02284d6 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -129,7 +129,7 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
mTestClass = new TestClass(testClass);
- Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
+ Log.v(TAG, "RavenwoodAwareTestRunner initializing for " + testClass.getCanonicalName());
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
@@ -146,7 +146,9 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
Object instance) {
- Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ }
for (var method : mTestClass.getAnnotatedMethods(annotationClass)) {
ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
@@ -169,12 +171,14 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
RavenwoodTestStats.getInstance().attachToRunNotifier(notifier);
if (mRealRunner instanceof ClassSkippingTestRunner) {
- Log.i(TAG, "onClassSkipped: description=" + description);
+ Log.v(TAG, "onClassSkipped: description=" + description);
mRealRunner.run(notifier);
return;
}
- Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "Running " + mTestJavaClass.getCanonicalName());
+ }
if (RAVENWOOD_VERBOSE_LOGGING) {
dumpDescription(description);
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 9eff20ad70e6..a3326337d485 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -132,36 +132,27 @@ public class RavenwoodContext extends RavenwoodBaseContext {
@Override
public Looper getMainLooper() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getLooper();
}
@Override
public Handler getMainThreadHandler() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getThreadHandler();
}
@Override
public Executor getMainExecutor() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getThreadExecutor();
}
@Override
public String getPackageName() {
- return Objects.requireNonNull(mPackageName,
- "Test must request setPackageName() (or setTargetPackageName())"
- + " via RavenwoodConfig");
+ return mPackageName;
}
@Override
public String getOpPackageName() {
- return Objects.requireNonNull(mPackageName,
- "Test must request setPackageName() via RavenwoodConfig");
+ return mPackageName;
}
@Override
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index a5d0bfd51a0f..70bc52bdaa12 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -15,6 +15,8 @@
*/
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -59,16 +61,22 @@ public final class RavenwoodRunnerState {
private Description mMethodDescription;
public void enterTestRunner() {
- Log.i(TAG, "enterTestRunner: " + mRunner);
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "enterTestRunner: " + mRunner);
+ }
RavenwoodRuntimeEnvironmentController.initForRunner();
}
public void enterTestClass() {
- Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+ }
}
public void exitTestClass() {
- Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+ }
assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty());
RavenwoodRuntimeEnvironmentController.exitTestClass();
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 930914f586eb..3cb6c5a6bd16 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -552,7 +552,7 @@ public class RavenwoodRuntimeEnvironmentController {
}
private static void dumpCommandLineArgs() {
- Log.i(TAG, "JVM arguments:");
+ Log.v(TAG, "JVM arguments:");
// Note, we use the wrapper in JUnit4, not the actual class (
// java.lang.management.ManagementFactory), because we can't see the later at the build
@@ -561,7 +561,7 @@ public class RavenwoodRuntimeEnvironmentController {
var args = ManagementFactory.getRuntimeMXBean().getInputArguments();
for (var arg : args) {
- Log.i(TAG, " " + arg);
+ Log.v(TAG, " " + arg);
}
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index fac07910be11..70c161c1f19a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -84,7 +84,7 @@ public class RavenwoodSystemProperties {
var ravenwoodProps = readProperties(path + RAVENWOOD_BUILD_PROP);
var deviceProps = readProperties(path + DEVICE_BUILD_PROP);
- Log.i(TAG, "Default system properties:");
+ Log.v(TAG, "Default system properties:");
ravenwoodProps.forEach((key, origValue) -> {
final String value;
@@ -100,7 +100,7 @@ public class RavenwoodSystemProperties {
} else {
value = origValue;
}
- Log.i(TAG, key + "=" + value);
+ Log.v(TAG, key + "=" + value);
sDefaultValues.put(key, value);
});
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
index f3688d664142..359210582ba5 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -15,6 +15,8 @@
*/
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+
import android.platform.test.annotations.internal.InnerRunner;
import android.util.Log;
@@ -53,7 +55,9 @@ abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable
}
try {
- Log.i(TAG, "Initializing the inner runner: " + runnerClass);
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "Initializing the inner runner: " + runnerClass);
+ }
try {
return runnerClass.getConstructor(Class.class)
.newInstance(testClass.getJavaClass());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
deleted file mode 100644
index 3ed0f50434fb..000000000000
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ /dev/null
@@ -1,139 +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 android.platform.test.ravenwood;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it.
- */
-@Deprecated
-public final class RavenwoodConfig {
- /**
- * Use this to mark a field as the configuration.
- * @hide
- */
- @Target({ElementType.FIELD})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Config {
- }
-
- /**
- * Stores internal states / methods associated with this config that's only needed in
- * junit-impl.
- */
- private RavenwoodConfig() {
- }
-
- /**
- * Return if the current process is running on a Ravenwood test environment.
- */
- public static boolean isOnRavenwood() {
- return RavenwoodRule.isOnRavenwood();
- }
-
- public static class Builder {
- private final RavenwoodConfig mConfig = new RavenwoodConfig();
-
- public Builder() {
- }
-
- /**
- * @deprecated no longer used. We always use an app UID.
- */
- @Deprecated
- public Builder setProcessSystem() {
- return this;
- }
-
- /**
- * @deprecated no longer used. We always use an app UID.
- */
- @Deprecated
- public Builder setProcessApp() {
- return this;
- }
-
- /**
- * @deprecated no longer used. Package name is set in the build file. (for now)
- */
- @Deprecated
- public Builder setPackageName(@NonNull String packageName) {
- return this;
- }
-
- /**
- * @deprecated no longer used. Package name is set in the build file. (for now)
- */
- @Deprecated
- public Builder setTargetPackageName(@NonNull String packageName) {
- return this;
- }
-
-
- /**
- * @deprecated no longer used. Target SDK level is set in the build file. (for now)
- */
- @Deprecated
- public Builder setTargetSdkLevel(int sdkLevel) {
- return this;
- }
-
- /**
- * @deprecated no longer used. Main thread is always available.
- */
- @Deprecated
- public Builder setProvideMainThread(boolean provideMainThread) {
- return this;
- }
-
- /**
- * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyImmutable(String, Object)}
- */
- @Deprecated
- public Builder setSystemPropertyImmutable(@NonNull String key,
- @Nullable Object value) {
- return this;
- }
-
- /**
- * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyMutable(String, Object)}
- */
- @Deprecated
- public Builder setSystemPropertyMutable(@NonNull String key,
- @Nullable Object value) {
- return this;
- }
-
- /**
- * @deprecated no longer used. All supported services are available.
- */
- @Deprecated
- public Builder setServicesRequired(@NonNull Class<?>... services) {
- return this;
- }
-
- public RavenwoodConfig build() {
- return mConfig;
- }
- }
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index d8cde0e029c9..ffe5f6c8c579 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -36,10 +36,8 @@ import java.util.Objects;
import java.util.regex.Pattern;
/**
- * @deprecated This class is undergoing a major change. Reach out to g/ravenwood if you need
- * any featues in it.
+ * Reach out to g/ravenwood if you need any features in it.
*/
-@Deprecated
public final class RavenwoodRule implements TestRule {
private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
index dbbc3453b2f1..391c5d56b212 100644
--- a/ravenwood/runtime-jni/ravenwood_initializer.cpp
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -140,7 +140,7 @@ static void check_system_property_access(const char* key, bool write) {
if (gVM != nullptr && gRunnerState != nullptr) {
JNIEnv* env;
if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
- ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
+ ALOGV("%s access to system property '%s'", write ? "Write" : "Read", key);
env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
return;
@@ -208,7 +208,7 @@ static const JNINativeMethod sMethods[] = {
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- ALOGI("%s: JNI_OnLoad", __FILE__);
+ ALOGV("%s: JNI_OnLoad", __FILE__);
maybeRedirectLog();
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index bab4c7e0fd14..8d8ed7119e84 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -204,7 +204,7 @@ static const JNINativeMethod sMethods[] =
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- ALOGI("%s: JNI_OnLoad", __FILE__);
+ ALOGV("%s: JNI_OnLoad", __FILE__);
JNIEnv* env = GetJNIEnvOrDie(vm);
g_StructStat = FindGlobalClassOrDie(env, "android/system/StructStat");
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
index bdc1de0c44f4..c08d4aa799a5 100755
--- a/ravenwood/scripts/extract-last-soong-commands.py
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -48,6 +48,7 @@ def main(args):
with open(outfile, "w") as out:
out.write(HEADER)
+ count = 0
with gzip.open(log) as f:
for line in f:
s = line.decode("utf-8")
@@ -63,7 +64,8 @@ def main(args):
if m:
command = m.groups()[0]
- out.write('#========\n')
+ count += 1
+ out.write(f'### Command {count} ========\n')
# Show the full command line before executing it.
out.write('#echo ' + shlex.quote(command) + '\n')
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
deleted file mode 100644
index 306c2b39c70d..000000000000
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
+++ /dev/null
@@ -1,42 +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.ravenwoodtest.bivalenttest;
-
-import static android.platform.test.ravenwood.RavenwoodConfig.isOnRavenwood;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test to make sure the config field is used.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodConfigTest {
- private static final String PACKAGE_NAME = "com.android.ravenwoodtest.bivalenttest";
-
- @Test
- public void testConfig() {
- assumeTrue(isOnRavenwood());
- assertEquals(PACKAGE_NAME,
- InstrumentationRegistry.getInstrumentation().getContext().getPackageName());
- }
-}
diff --git a/services/Android.bp b/services/Android.bp
index fc0bb33e6e4e..3d512f782232 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -210,6 +210,34 @@ crashrecovery_java_defaults {
},
}
+soong_config_module_type {
+ name: "ondeviceintelligence_module_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_ondevice_intelligence_module",
+ "release_ondevice_intelligence_platform",
+ ],
+ properties: [
+ "libs",
+ "srcs",
+ "static_libs",
+ ],
+}
+
+// Conditionally add ondeviceintelligence stubs library
+ondeviceintelligence_module_java_defaults {
+ name: "ondeviceintelligence_conditionally",
+ soong_config_variables: {
+ release_ondevice_intelligence_module: {
+ libs: ["service-ondeviceintelligence.stubs.system_server"],
+ },
+ release_ondevice_intelligence_platform: {
+ srcs: [":service-ondeviceintelligence-sources-platform"],
+ },
+ },
+}
+
// merge all required services into one jar
// ============================================================
soong_config_module_type {
@@ -236,6 +264,7 @@ system_java_library {
"services_java_defaults",
"art_profile_java_defaults",
"services_crashrecovery_stubs_conditionally",
+ "ondeviceintelligence_conditionally",
],
installable: true,
@@ -282,9 +311,11 @@ system_java_library {
"services.wifi",
"service-blobstore",
"service-jobscheduler",
- "service-connectivity-b-pre-jarjar", // Move it to mainline module
"android.hidl.base-V1.0-java",
- ],
+ ] + select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), {
+ true: [],
+ default: ["service-connectivity-b-platform"],
+ }),
libs: [
"android.hidl.manager-V1.0-java",
diff --git a/services/appfunctions/Android.bp b/services/appfunctions/Android.bp
index eb6e46861898..7337aa26c145 100644
--- a/services/appfunctions/Android.bp
+++ b/services/appfunctions/Android.bp
@@ -19,6 +19,7 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [
":services.appfunctions-sources",
+ ":statslog-appfunctions-java-gen",
"java/**/*.logtags",
],
libs: ["services.core"],
@@ -26,3 +27,10 @@ java_library_static {
baseline_filename: "lint-baseline.xml",
},
}
+
+genrule {
+ name: "statslog-appfunctions-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module appfunctions --javaPackage com.android.server.appfunctions --javaClass AppFunctionsStatsLog --minApiLevel 35",
+ out: ["java/com/android/server/appfunctions/AppFunctionsStatsLog.java"],
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index 81e83b563945..eaea4435099c 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -16,6 +16,8 @@
package com.android.server.appfunctions;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -35,6 +37,11 @@ public final class AppFunctionExecutors {
/* workQueue= */ new LinkedBlockingQueue<>(),
new NamedThreadFactory("AppFunctionExecutors"));
+ /** Executor for stats logging. */
+ public static final ExecutorService LOGGING_THREAD_EXECUTOR =
+ Executors.newSingleThreadExecutor(
+ new NamedThreadFactory("AppFunctionsLoggingExecutors"));
+
static {
THREAD_POOL_EXECUTOR.allowCoreThreadTimeOut(true);
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index f13e22950e2d..669025f071c0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -24,12 +24,13 @@ import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_E
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.AppFunctionManager;
import android.app.appfunctions.AppFunctionManagerHelper;
import android.app.appfunctions.AppFunctionRuntimeMetadata;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
-import android.app.appfunctions.AppFunctionException;
+import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.app.appfunctions.IAppFunctionEnabledCallback;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appfunctions.IAppFunctionService;
@@ -85,6 +86,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
private final ServiceConfig mServiceConfig;
private final Context mContext;
private final Map<String, Object> mLocks = new WeakHashMap<>();
+ private final AppFunctionsLoggerWrapper mLoggerWrapper;
public AppFunctionManagerServiceImpl(@NonNull Context context) {
this(
@@ -93,7 +95,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
context, IAppFunctionService.Stub::asInterface, THREAD_POOL_EXECUTOR),
new CallerValidatorImpl(context),
new ServiceHelperImpl(context),
- new ServiceConfigImpl());
+ new ServiceConfigImpl(),
+ new AppFunctionsLoggerWrapper(context));
}
@VisibleForTesting
@@ -102,12 +105,14 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
RemoteServiceCaller<IAppFunctionService> remoteServiceCaller,
CallerValidator callerValidator,
ServiceHelper appFunctionInternalServiceHelper,
- ServiceConfig serviceConfig) {
+ ServiceConfig serviceConfig,
+ AppFunctionsLoggerWrapper loggerWrapper) {
mContext = Objects.requireNonNull(context);
mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
mCallerValidator = Objects.requireNonNull(callerValidator);
mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
mServiceConfig = serviceConfig;
+ mLoggerWrapper = loggerWrapper;
}
/** Called when the user is unlocked. */
@@ -146,8 +151,27 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
Objects.requireNonNull(requestInternal);
Objects.requireNonNull(executeAppFunctionCallback);
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+
final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
- new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
+ new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback,
+ new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() {
+ @Override
+ public void finalizeOnSuccess(
+ @NonNull ExecuteAppFunctionResponse result,
+ long executionStartTimeMillis) {
+ mLoggerWrapper.logAppFunctionSuccess(requestInternal, result,
+ callingUid, executionStartTimeMillis);
+ }
+
+ @Override
+ public void finalizeOnError(@NonNull AppFunctionException error,
+ long executionStartTimeMillis) {
+ mLoggerWrapper.logAppFunctionError(requestInternal,
+ error.getErrorCode(), callingUid, executionStartTimeMillis);
+ }
+ });
String validatedCallingPackage;
try {
@@ -158,14 +182,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
} catch (SecurityException exception) {
safeExecuteAppFunctionCallback.onError(
new AppFunctionException(
- AppFunctionException.ERROR_DENIED,
- exception.getMessage()));
+ AppFunctionException.ERROR_DENIED, exception.getMessage()));
return null;
}
- int callingUid = Binder.getCallingUid();
- int callingPid = Binder.getCallingPid();
-
ICancellationSignal localCancelTransport = CancellationSignal.createTransport();
THREAD_POOL_EXECUTOR.execute(
@@ -195,12 +215,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
@NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
@NonNull IBinder callerBinder) {
UserHandle targetUser = requestInternal.getUserHandle();
- // TODO(b/354956319): Add and honor the new enterprise policies.
- if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
+ UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
+ if (!mCallerValidator.verifyEnterprisePolicyIsAllowed(callingUser, targetUser)) {
safeExecuteAppFunctionCallback.onError(
- new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
- "Cannot run on a device with a device owner or from the managed"
- + " profile."));
+ new AppFunctionException(
+ AppFunctionException.ERROR_ENTERPRISE_POLICY_DISALLOWED,
+ "Cannot run on a user with a restricted enterprise policy"));
return;
}
@@ -442,7 +462,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
if (!bindServiceResult) {
Slog.e(TAG, "Failed to bind to the AppFunctionService");
safeExecuteAppFunctionCallback.onError(
- new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
+ new AppFunctionException(
+ AppFunctionException.ERROR_SYSTEM_ERROR,
"Failed to bind the AppFunctionService."));
}
}
@@ -495,8 +516,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
return;
}
FutureGlobalSearchSession futureGlobalSearchSession =
- new FutureGlobalSearchSession(
- perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR);
+ new FutureGlobalSearchSession(perUserAppSearchManager, THREAD_POOL_EXECUTOR);
AppFunctionMetadataObserver appFunctionMetadataObserver =
new AppFunctionMetadataObserver(
user.getUserHandle(),
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java
new file mode 100644
index 000000000000..7ba1bbc536f6
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.appfunctions;
+
+import static com.android.server.appfunctions.AppFunctionExecutors.LOGGING_THREAD_EXECUTOR;
+
+import android.annotation.NonNull;
+import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
+import android.app.appfunctions.ExecuteAppFunctionResponse;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/** Wraps AppFunctionsStatsLog. */
+public class AppFunctionsLoggerWrapper {
+ private static final String TAG = AppFunctionsLoggerWrapper.class.getSimpleName();
+
+ private static final int SUCCESS_RESPONSE_CODE = -1;
+
+ private final Context mContext;
+
+ public AppFunctionsLoggerWrapper(@NonNull Context context) {
+ mContext = Objects.requireNonNull(context);
+ }
+
+ void logAppFunctionSuccess(ExecuteAppFunctionAidlRequest request,
+ ExecuteAppFunctionResponse response, int callingUid, long executionStartTimeMillis) {
+ logAppFunctionsRequestReported(request, SUCCESS_RESPONSE_CODE,
+ response.getResponseDataSize(), callingUid, executionStartTimeMillis);
+ }
+
+ void logAppFunctionError(ExecuteAppFunctionAidlRequest request, int errorCode, int callingUid,
+ long executionStartTimeMillis) {
+ logAppFunctionsRequestReported(request, errorCode, /* responseSizeBytes = */ 0, callingUid,
+ executionStartTimeMillis);
+ }
+
+ private void logAppFunctionsRequestReported(ExecuteAppFunctionAidlRequest request,
+ int errorCode, int responseSizeBytes, int callingUid, long executionStartTimeMillis) {
+ final long e2eRequestLatencyMillis =
+ SystemClock.elapsedRealtime() - request.getRequestTime();
+ final long requestOverheadMillis =
+ executionStartTimeMillis > 0 ? (executionStartTimeMillis - request.getRequestTime())
+ : e2eRequestLatencyMillis;
+ LOGGING_THREAD_EXECUTOR.execute(() -> AppFunctionsStatsLog.write(
+ AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED,
+ /* callerPackageUid= */ callingUid,
+ /* targetPackageUid= */
+ getPackageUid(request.getClientRequest().getTargetPackageName()),
+ /* errorCode= */ errorCode,
+ /* requestSizeBytes= */ request.getClientRequest().getRequestDataSize(),
+ /* responseSizeBytes= */ responseSizeBytes,
+ /* requestDurationMs= */ e2eRequestLatencyMillis,
+ /* requestOverheadMs= */ requestOverheadMillis)
+ );
+ }
+
+ private int getPackageUid(String packageName) {
+ try {
+ return mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Package uid not found for " + packageName);
+ }
+ return 0;
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 5393b939b5ed..61917676e88d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -81,10 +81,12 @@ public interface CallerValidator {
@NonNull String functionId);
/**
- * Checks if the user is organization managed.
+ * Checks if the app function policy is allowed.
*
+ * @param callingUser The current calling user.
* @param targetUser The user which the caller is requesting to execute as.
- * @return Whether the user is organization managed.
+ * @return Whether the app function policy is allowed.
*/
- boolean isUserOrganizationManaged(@NonNull UserHandle targetUser);
+ boolean verifyEnterprisePolicyIsAllowed(
+ @NonNull UserHandle callingUser, @NonNull UserHandle targetUser);
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index e85a70d5845a..69481c32baf0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -28,6 +28,7 @@ import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchManager.SearchContext;
@@ -39,7 +40,6 @@ import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
import com.android.internal.infra.AndroidFuture;
@@ -124,8 +124,7 @@ class CallerValidatorImpl implements CallerValidator {
FutureAppSearchSession futureAppSearchSession =
new FutureAppSearchSessionImpl(
Objects.requireNonNull(
- mContext
- .createContextAsUser(targetUser, 0)
+ mContext.createContextAsUser(targetUser, 0)
.getSystemService(AppSearchManager.class)),
THREAD_POOL_EXECUTOR,
new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
@@ -168,13 +167,16 @@ class CallerValidatorImpl implements CallerValidator {
}
@Override
- public boolean isUserOrganizationManaged(@NonNull UserHandle targetUser) {
- if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
- .isDeviceManaged()) {
- return true;
- }
- return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
- .isManagedProfile(targetUser.getIdentifier());
+ public boolean verifyEnterprisePolicyIsAllowed(
+ @NonNull UserHandle callingUser, @NonNull UserHandle targetUser) {
+ @AppFunctionsPolicy
+ int callingUserPolicy = getDevicePolicyManagerAsUser(callingUser).getAppFunctionsPolicy();
+ @AppFunctionsPolicy
+ int targetUserPolicy = getDevicePolicyManagerAsUser(targetUser).getAppFunctionsPolicy();
+ boolean isSameUser = callingUser.equals(targetUser);
+
+ return isAppFunctionPolicyAllowed(targetUserPolicy, isSameUser)
+ && isAppFunctionPolicyAllowed(callingUserPolicy, isSameUser);
}
/**
@@ -264,4 +266,18 @@ class CallerValidatorImpl implements CallerValidator {
return Process.INVALID_UID;
}
}
+
+ private boolean isAppFunctionPolicyAllowed(
+ @AppFunctionsPolicy int userPolicy, boolean isSameUser) {
+ return switch (userPolicy) {
+ case DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY -> true;
+ case DevicePolicyManager.APP_FUNCTIONS_DISABLED_CROSS_PROFILE -> isSameUser;
+ default -> false;
+ };
+ }
+
+ private DevicePolicyManager getDevicePolicyManagerAsUser(@NonNull UserHandle targetUser) {
+ return mContext.createContextAsUser(targetUser, /* flags= */ 0)
+ .getSystemService(DevicePolicyManager.class);
+ }
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
index c689bb92f8f7..a5ae7e310022 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
@@ -16,8 +16,8 @@
package com.android.server.appfunctions;
import android.annotation.NonNull;
-import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.AppFunctionException;
+import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.app.appfunctions.IAppFunctionService;
import android.app.appfunctions.ICancellationCallback;
@@ -52,6 +52,7 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp
@NonNull IAppFunctionService service,
@NonNull ServiceUsageCompleteListener serviceUsageCompleteListener) {
try {
+ mSafeExecuteAppFunctionCallback.setExecutionStartTimeMillis();
service.executeAppFunction(
mRequestInternal.getClientRequest(),
mRequestInternal.getCallingPackage(),
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 762665c00e05..cffdfbd36532 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -330,6 +330,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Handler to the background thread that saves states to disk.
private Handler mSaveStateHandler;
+
+ private Handler mAlarmHandler;
// Handler to the background thread that saves generated previews to disk. All operations that
// modify saved previews must be run on this Handler.
private Handler mSavePreviewsHandler;
@@ -373,6 +375,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (removeAppWidgetServiceIoFromCriticalPath()) {
mSaveStateHandler = new Handler(BackgroundThread.get().getLooper(),
this::handleSaveMessage);
+ mAlarmHandler = new Handler(BackgroundThread.get().getLooper());
} else {
mSaveStateHandler = BackgroundThread.getHandler();
}
@@ -2739,10 +2742,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
if (provider.broadcast != null) {
final PendingIntent broadcast = provider.broadcast;
- mSaveStateHandler.post(() -> {
- mAlarmManager.cancel(broadcast);
- broadcast.cancel();
- });
+ Runnable cancelRunnable = () -> {
+ mAlarmManager.cancel(broadcast);
+ broadcast.cancel();
+ };
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mAlarmHandler.post(cancelRunnable);
+ } else {
+ mSaveStateHandler.post(cancelRunnable);
+ }
provider.broadcast = null;
}
}
@@ -3422,10 +3430,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// invariant and established the PendingIntent safely.
final long period = Math.max(info.updatePeriodMillis, MIN_UPDATE_PERIOD);
final PendingIntent broadcast = provider.broadcast;
- mSaveStateHandler.post(() ->
+
+ Runnable repeatRunnable = () -> {
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, broadcast)
- );
+ SystemClock.elapsedRealtime() + period, period, broadcast);
+ };
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mAlarmHandler.post(repeatRunnable);
+ } else {
+ mSaveStateHandler.post(repeatRunnable);
+ }
}
}
}
diff --git a/services/art-wear-profile b/services/art-wear-profile
index 1e3090f9bf00..f080715643ca 100644
--- a/services/art-wear-profile
+++ b/services/art-wear-profile
@@ -7419,7 +7419,7 @@ PLcom/android/server/app/GameManagerService;->sendUserMessage(IILjava/lang/Strin
PLcom/android/server/app/GameManagerService;->updateConfigsForUser(IZ[Ljava/lang/String;)V
PLcom/android/server/app/GameManagerService;->writeGameModeInterventionsToFile(I)V
PLcom/android/server/app/GameManagerSettings;-><init>(Ljava/io/File;)V
-HPLcom/android/server/app/GameManagerSettings;->getConfigOverride(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration;
+HPLcom/android/server/app/GameManagerSettings;->getConfigOverrideLocked(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration;
HPLcom/android/server/app/GameManagerSettings;->getGameModeLocked(Ljava/lang/String;)I
PLcom/android/server/app/GameManagerSettings;->readPersistentDataLocked()Z
PLcom/android/server/appbinding/AppBindingConstants;-><init>(Ljava/lang/String;)V
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5e1b1473d233..7c5cfa91ab8a 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -9,6 +9,16 @@ flag {
}
flag {
+ name: "improve_fill_dialog_aconfig"
+ namespace: "autofill"
+ description: "Improvements for Fill Dialog. Guard DeviceConfig rollout "
+ bug: "382493181"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fill_fields_from_current_session_only"
namespace: "autofill"
description: "Only fill autofill fields that are part of the current session."
@@ -76,3 +86,13 @@ flag {
description: "Highlight single field after autofill selection"
bug: "41496744"
}
+
+flag {
+ name: "metrics_fixes"
+ namespace: "autofill"
+ description: "Fixes various framework reported metrics"
+ bug: "362581326, 363011343"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 219b788448e8..5e7e557d7041 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -354,6 +354,13 @@ final class AutofillInlineSuggestionsRequestSession {
}
}
+ private void handleOnInputMethodStartInputView() {
+ synchronized (mLock) {
+ mUiCallback.onInputMethodStartInputView();
+ handleOnReceiveImeStatusUpdated(true, true);
+ }
+ }
+
/**
* Handles the IME session status received from the IME.
*
@@ -437,8 +444,8 @@ final class AutofillInlineSuggestionsRequestSession {
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
- AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
- session, true, true));
+ AutofillInlineSuggestionsRequestSession::handleOnInputMethodStartInputView,
+ session));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 259ea148f163..cba8c66cd5e3 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2139,6 +2139,32 @@ public final class AutofillManagerService
}
@Override
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationStart(sessionId, startTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationEnd(sessionId, endTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5cf96bfb2b8b..0fa43ac7091b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -823,6 +823,36 @@ final class AutofillManagerServiceImpl
}
@GuardedBy("mLock")
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationStart(startTimeMs);
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationEnd(endTimeMs);
+ }
+
+ @GuardedBy("mLock")
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index bd1b0ea99e17..6ccf5e47ca6c 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -45,6 +45,7 @@ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
@@ -98,6 +99,7 @@ public final class PresentationStatsEventLogger {
NOT_SHOWN_REASON_REQUEST_FAILED,
NOT_SHOWN_REASON_NO_FOCUS,
NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
+ NOT_SHOWN_REASON_SUGGESTION_FILTERED,
NOT_SHOWN_REASON_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
@@ -178,6 +180,8 @@ public final class PresentationStatsEventLogger {
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
public static final int NOT_SHOWN_REASON_UNKNOWN =
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
+ public static final int NOT_SHOWN_REASON_SUGGESTION_FILTERED =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
public static final int AUTHENTICATION_TYPE_UNKNOWN =
AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
@@ -286,12 +290,43 @@ public final class PresentationStatsEventLogger {
});
}
+ /**
+ * Call this when first entering the View. It will check if there are pre-existing characters
+ * in the view, and sets NOT_SHOWN_REASON_SUGGESTION_FILTERED if there is
+ */
+ public void maybeSetNoPresentationEventReasonSuggestionsFiltered(AutofillValue value) {
+ mEventInternal.ifPresent(
+ event -> {
+ if (value == null || !value.isText()) {
+ return;
+ }
+
+ int length = value.getTextValue().length();
+
+ if (length > 0) {
+ maybeSetNoPresentationEventReason(NOT_SHOWN_REASON_SUGGESTION_FILTERED);
+ }
+ });
+ }
+
public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) {
- mEventInternal.ifPresent(event -> {
- if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) {
- event.mNoPresentationReason = reason;
- }
- });
+ mEventInternal.ifPresent(
+ event -> {
+ if (event.mCountShown != 0) {
+ return;
+ }
+
+ // The only events that can be overwritten.
+ // NOT_SHOWN_REASON_UNKNOWN is the default for inline/dropdown
+ // NOT_SHOWN_REASON_NO_FOCUS is the default for fill dialog
+ if (event.mNoPresentationReason != NOT_SHOWN_REASON_UNKNOWN
+ || event.mNoPresentationReason != NOT_SHOWN_REASON_NO_FOCUS) {
+ Slog.d(TAG, "Not setting no presentation reason because it already exists");
+ return;
+ }
+
+ event.mNoPresentationReason = reason;
+ });
}
public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6b227d7a876e..ba9865d513d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -40,6 +40,8 @@ import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.service.autofill.Flags.highlightAutofillSingleField;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
+import static android.service.autofill.Flags.metricsFixes;
import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -50,6 +52,7 @@ import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -184,6 +187,7 @@ import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -195,6 +199,7 @@ import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -248,6 +253,8 @@ final class Session
private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
+ private static final long DEFAULT_UNASSIGNED_TIME = -1;
+
static final String SESSION_ID_KEY = "autofill_session_id";
static final String REQUEST_ID_KEY = "autofill_request_id";
@@ -292,6 +299,31 @@ final class Session
@Retention(RetentionPolicy.SOURCE)
@interface SessionState {}
+ /**
+ * Indicates fill dialog will not be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_NO = 0;
+
+ /**
+ * Indicates fill dialog is shown.
+ */
+ private static final int SHOW_FILL_DIALOG_YES = 1;
+
+ /**
+ * Indicates fill dialog can be shown, but we need to wait.
+ * For eg, if the IME animation is happening, we need for it to complete before fill dialog can
+ * be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_WAIT = 2;
+
+ @IntDef(prefix = { "SHOW_FILL_DIALOG_" }, value = {
+ SHOW_FILL_DIALOG_NO,
+ SHOW_FILL_DIALOG_YES,
+ SHOW_FILL_DIALOG_WAIT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ShowFillDialogState{}
+
@GuardedBy("mLock")
private final SessionFlags mSessionFlags;
@@ -576,6 +608,47 @@ final class Session
private boolean mIgnoreViewStateResetToEmpty;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ * Configured via device config flag and aconfig flag.
+ */
+ private final boolean mImproveFillDialogEnabled;
+
+ /**
+ * Timeout, after which, fill dialog won't be shown.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogTimeoutMs;
+
+ /**
+ * Time to wait after ime Animation ends before showing up fill dialog.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogMinWaitAfterImeAnimationMs;
+
+ /**
+ * Indicates the need to wait for ime animation to end before showing up fill dialog.
+ * This is set when we receive notification of ime animation start.
+ * Focussing on one input field from another wouldn't cause ime to re-animate. So this will let
+ * us know whether we need to wait for ime animation finish notification.
+ */
+ private boolean mWaitForImeAnimation;
+
+ /**
+ * A runnable set to run when there is a need to wait for ime animation to end before showing
+ * up fill dialog. This is set only if the fill response has been received, and the response
+ * is eligible for showing up fill dialog, but the ime animation hasn't completed. This allows
+ * for this runnable to be scheduled/run when the ime animation ends, in order to show fill
+ * dialog.
+ */
+ private Runnable mFillDialogRunnable;
+
+ private long mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+
/*
* Id of the previous view that was entered. Once set, it would only be replaced by non-null
* view ids.
@@ -1493,6 +1566,12 @@ final class Session
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
+ if (mImproveFillDialogEnabled) {
+ // New request has been sent, so re-enable fill dialog.
+ // Fill dialog is eligible to be shown after each Fill request.
+ enableFillDialog();
+ }
+
return Optional.of(requestId);
}
@@ -1657,6 +1736,11 @@ final class Session
mSaveEventLogger = SaveEventLogger.forSessionId(sessionId, mLatencyBaseTime);
mIsPrimaryCredential = isPrimaryCredential;
mIgnoreViewStateResetToEmpty = AutofillFeatureFlags.shouldIgnoreViewStateResetToEmpty();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ mFillDialogTimeoutMs = AutofillFeatureFlags.getFillDialogTimeoutMs();
+ mFillDialogMinWaitAfterImeAnimationMs =
+ AutofillFeatureFlags.getFillDialogMinWaitAfterImeAnimationtEndMs();
synchronized (mLock) {
mSessionFlags = new SessionFlags();
@@ -1682,6 +1766,13 @@ final class Session
public void notifyInlineUiHidden(AutofillId autofillId) {
notifyFillUiHidden(autofillId);
}
+
+ @Override
+ public void onInputMethodStartInputView() {
+ // TODO(b/377868687): This method isn't called when IME doesn't
+ // support inline suggestion. Handle those cases as well.
+ onInputMethodStart();
+ }
});
mMetricsLogger.write(
@@ -3044,6 +3135,12 @@ final class Session
}
}
+ private void onInputMethodStart() {
+ synchronized (mLock) {
+ mLastInputStartTime = SystemClock.elapsedRealtime();
+ }
+ }
+
private void doStartIntentSender(IntentSender intentSender, Intent intent) {
try {
synchronized (mLock) {
@@ -3645,8 +3742,13 @@ final class Session
final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
if (lastResponse == null) return;
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+ if (metricsFixes()) {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
+ PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+ } else {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+ }
mPresentationStatsEventLogger.logAndEndEvent("Context committed");
final int flags = lastResponse.getFlags();
@@ -5029,6 +5131,13 @@ final class Session
mPreviouslyFillDialogPotentiallyStarted = false;
} else {
mPreviouslyFillDialogPotentiallyStarted = true;
+ if (metricsFixes()) {
+ // Set the default reason for now if the user doesn't trigger any focus
+ // event on the autofillable view. This can be changed downstream when
+ // more information is available or session is committed.
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_NO_FOCUS);
+ }
}
Optional<Integer> maybeRequestId =
requestNewFillResponseLocked(
@@ -5195,6 +5304,10 @@ final class Session
if (maybeNewRequestId.isPresent()) {
mPresentationStatsEventLogger.maybeSetRequestId(maybeNewRequestId.get());
}
+ if (metricsFixes()) {
+ mPresentationStatsEventLogger
+ .maybeSetNoPresentationEventReasonSuggestionsFiltered(value);
+ }
}
logPresentationStatsOnViewEnteredLocked(
@@ -5229,8 +5342,14 @@ final class Session
// It's not necessary that there's no more presentation for this view. It could
// be that the user chose some suggestion, in which case, view exits.
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+ if (metricsFixes()) {
+ mPresentationStatsEventLogger
+ .maybeSetNoPresentationEventReasonIfNoReasonExists(
+ NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+ } else {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+ }
}
break;
default:
@@ -5407,6 +5526,15 @@ final class Session
}
}
+ private void resetImeAnimationState() {
+ synchronized (mLock) {
+ mWaitForImeAnimation = false;
+ mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+ }
+ }
+
@Override
public void onFillReady(
@NonNull FillResponse response,
@@ -5452,18 +5580,24 @@ final class Session
final AutofillId[] ids = response.getFillDialogTriggerIds();
if (ids != null && ArrayUtils.contains(ids, filledId)) {
- if (requestShowFillDialog(response, filledId, filterText, flags)) {
+ @ShowFillDialogState int fillDialogState =
+ requestShowFillDialog(response, filledId, filterText, flags);
+ if (fillDialogState == SHOW_FILL_DIALOG_YES) {
synchronized (mLock) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
}
- // Just show fill dialog once, so disabled after shown.
+ // Just show fill dialog once per fill request, so disabled after shown.
// Note: Cannot disable before requestShowFillDialog() because the method
- // need to check whether fill dialog enabled.
+ // need to check whether fill dialog is enabled.
setFillDialogDisabled();
+ resetImeAnimationState();
return;
- } else {
+ } else if (fillDialogState == SHOW_FILL_DIALOG_NO) {
+ resetImeAnimationState();
setFillDialogDisabled();
+ } else { // SHOW_FILL_DIALOG_WAIT
+ return;
}
}
@@ -5559,7 +5693,20 @@ final class Session
}
}
+ private void enableFillDialog() {
+ if (sVerbose) {
+ Slog.v(TAG, "Enabling Fill Dialog....");
+ }
+ synchronized (mLock) {
+ mSessionFlags.mFillDialogDisabled = false;
+ }
+ notifyClientFillDialogTriggerIds(null);
+ }
+
private void setFillDialogDisabled() {
+ if (sVerbose) {
+ Slog.v(TAG, "Disabling Fill Dialog.");
+ }
synchronized (mLock) {
mSessionFlags.mFillDialogDisabled = true;
}
@@ -5577,24 +5724,28 @@ final class Session
}
}
- private boolean requestShowFillDialog(
+ private @ShowFillDialogState int requestShowFillDialog(
FillResponse response, AutofillId filledId, String filterText, int flags) {
if (!isFillDialogUiEnabled()) {
+ // TODO(b/377868687): The above check includes credman fields. We may want to show
+ // credman fields again.
// Unsupported fill dialog UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
- return false;
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): fill dialog is disabled");
+ return SHOW_FILL_DIALOG_NO;
}
- if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
- // IME is showing, fallback to normal suggestions UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing");
- return false;
- }
+ if (!mImproveFillDialogEnabled) {
+ if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
+ // IME is showing, fallback to normal suggestions UI
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): IME is showing");
+ return SHOW_FILL_DIALOG_NO;
+ }
- if (mInlineSessionController.isImeShowing()) {
- // IME is showing, fallback to normal suggestions UI
- // Note: only work when inline suggestions supported
- return false;
+ if (mInlineSessionController.isImeShowing()) {
+ // IME is showing, fallback to normal suggestions UI
+ // Note: only work when inline suggestions supported
+ return SHOW_FILL_DIALOG_NO;
+ }
}
synchronized (mLock) {
@@ -5602,29 +5753,84 @@ final class Session
|| !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
// Last fill dialog triggered ids are changed.
if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
- return false;
+ return SHOW_FILL_DIALOG_NO;
+ }
+
+ if (mImproveFillDialogEnabled && mInlineSessionController.isImeShowing()) {
+ long currentTimestampMs = SystemClock.elapsedRealtime();
+ long durationMs = currentTimestampMs - mLastInputStartTime;
+ if (sVerbose) {
+ Log.d(TAG, "IME is showing. Checking for elapsed time ");
+ Log.d(TAG, "IME is showing. Timestamps start: " + mLastInputStartTime
+ + " current: " + currentTimestampMs + " duration: " + durationMs
+ + " mFillDialogTimeoutMs: " + mFillDialogTimeoutMs);
+ }
+
+ // Following situations can arise wrt IME animation.
+ // 1. No animation happening (eg IME already animated). In that case,
+ // mWaitForImeAnimation should be false. This is possible if the IME is already up
+ // on a field, but the user focusses on another field. Under such condition,
+ // since IME has already animated, there won't be another animation. However,
+ // onInputStartInputView is still called.
+ // 2. Animation is still proceeding. We should wait for animation to finish,
+ // and then proceed.
+ // 3. Animation is complete.
+ if (mWaitForImeAnimation) {
+ // we need to wait for animation to happen. We can't return from here yet.
+ // This is the situation #2 described above.
+ Log.d(TAG, "Waiting for ime animation to complete before showing fill dialog");
+ mFillDialogRunnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
+
+ // Incorporate situations 1 & 3 discussed above. We calculate the duration from the
+ // max of start input time or the ime finish time
+ long effectiveDuration = currentTimestampMs
+ - Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs);
+ if (effectiveDuration >= mFillDialogTimeoutMs) {
+ Log.d(TAG, "Fill dialog not shown since IME has been up for more time than "
+ + mFillDialogTimeoutMs + "ms");
+ return SHOW_FILL_DIALOG_NO;
+ } else if (effectiveDuration < mFillDialogMinWaitAfterImeAnimationMs) {
+ // we need to wait for some time after animation ends
+ Runnable runnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ mHandler.postDelayed(runnable,
+ mFillDialogMinWaitAfterImeAnimationMs - effectiveDuration);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
}
}
+ showFillDialog(response, filledId, filterText);
+ return SHOW_FILL_DIALOG_YES;
+ }
+
+ private Runnable createFillDialogEvalRunnable(
+ @NonNull FillResponse response,
+ @NonNull AutofillId filledId,
+ String filterText,
+ int flags) {
+ return () -> {
+ synchronized (mLock) {
+ AutofillValue value = AutofillValue.forText(filterText);
+ onFillReady(response, filledId, value, flags);
+ }
+ };
+ }
+
+ private void showFillDialog(FillResponse response, AutofillId filledId, String filterText) {
Drawable serviceIcon = null;
+ PresentationStatsEventLogger logger = null;
synchronized (mLock) {
serviceIcon = getServiceIcon(response);
+ logger = mPresentationStatsEventLogger;
}
- getUiForShowing()
- .showFillDialog(
- filledId,
- response,
- filterText,
- mService.getServicePackageName(),
- mComponentName,
- serviceIcon,
- this,
- id,
- mCompatMode,
- mPresentationStatsEventLogger,
- mLock);
- return true;
+ getUiForShowing().showFillDialog(filledId, response, filterText,
+ mService.getServicePackageName(), mComponentName, serviceIcon, this,
+ id, mCompatMode, logger, mLock);
}
/**
@@ -6752,11 +6958,15 @@ final class Session
private void startNewEventForPresentationStatsEventLogger() {
synchronized (mLock) {
mPresentationStatsEventLogger.startNewEvent();
- // Set the default reason for now if the user doesn't trigger any focus event
- // on the autofillable view. This can be changed downstream when more
- // information is available or session is committed.
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_NO_FOCUS);
+ // This is a fill dialog only state, moved to when we set
+ // mPreviouslyFillDialogPotentiallyStarted = true
+ if (!metricsFixes()) {
+ // Set the default reason for now if the user doesn't trigger any focus event
+ // on the autofillable view. This can be changed downstream when more
+ // information is available or session is committed.
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_NO_FOCUS);
+ }
mPresentationStatsEventLogger.maybeSetDetectionPreference(
getDetectionPreferenceForLogging());
mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -7513,7 +7723,11 @@ final class Session
if (sVerbose) {
Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
}
- mSessionCommittedEventLogger.maybeSetCommitReason(val);
+ if (metricsFixes()) {
+ mSessionCommittedEventLogger.maybeSetCommitReasonIfUnset(val);
+ } else {
+ mSessionCommittedEventLogger.maybeSetCommitReason(val);
+ }
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
SystemClock.elapsedRealtime() - mStartTime);
@@ -7689,6 +7903,30 @@ final class Session
mSessionState = STATE_REMOVED;
}
+ @GuardedBy("mLock")
+ public void notifyImeAnimationStart(long startTimeMs) {
+ mImeAnimationStartTimeMs = startTimeMs;
+ mWaitForImeAnimation = true;
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(long endTimeMs) {
+ mImeAnimationFinishTimeMs = endTimeMs;
+ // Make sure to use mRunnable with synchronized
+ if (mFillDialogRunnable != null) {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, starting fill dialog.");
+ }
+ mHandler.postDelayed(mFillDialogRunnable, mFillDialogMinWaitAfterImeAnimationMs);
+ mFillDialogRunnable = null;
+ } else {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, no runnable present.");
+ }
+ }
+ mWaitForImeAnimation = false;
+ }
+
void onPendingSaveUi(int operation, @NonNull IBinder token) {
getUiForShowing().onPendingSaveUi(operation, token);
}
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 8f3c8803154d..7fd5648156a8 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -76,6 +76,17 @@ public final class SessionCommittedEventLogger {
});
}
+ /** Set commit_reason if not already set */
+ public void maybeSetCommitReasonIfUnset(@AutofillCommitReason int val) {
+ mEventInternal.ifPresent(
+ event -> {
+ if (event.mCommitReason != COMMIT_REASON_UNKNOWN) {
+ return;
+ }
+ event.mCommitReason = val;
+ });
+ }
+
/**
* Set session_duration_millis as long as mEventInternal presents.
*/
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index ffc80ee7d710..7287bdd8e34f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -409,5 +409,10 @@ public final class InlineFillUi {
* Callback to notify inline ui is hidden.
*/
void notifyInlineUiHidden(@NonNull AutofillId autofillId);
+
+ /**
+ * Callback to notify input method started.
+ */
+ void onInputMethodStartInputView();
}
}
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
index 4bcfb33b9e7c..7c8604f06b10 100644
--- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -240,9 +240,9 @@ class BackupRestoreProcessor {
boolean matchesMacAddress = Objects.equals(
associationInfo.getDeviceMacAddress(),
restored.getDeviceMacAddress());
- boolean matchesDeviceId = !Flags.associationTag()
- || (associationInfo.getDeviceId() != null
- && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
+ boolean matchesDeviceId = Flags.associationTag()
+ && (associationInfo.getDeviceId() != null
+ && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
return matchesMacAddress || matchesDeviceId;
};
AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
diff --git a/services/companion/java/com/android/server/companion/utils/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
index 8ea5c89116eb..83cbde6639c0 100644
--- a/services/companion/java/com/android/server/companion/utils/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -21,6 +21,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION;
@@ -31,6 +32,7 @@ import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_COMPUTER;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_GLASSES;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NULL;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WATCH;
import static com.android.internal.util.FrameworkStatsLog.write;
@@ -71,6 +73,10 @@ public final class MetricUtils {
DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING
);
+ map.put(
+ DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+ CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING
+ );
METRIC_DEVICE_PROFILE = unmodifiableMap(map);
}
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index f37e0c94caca..6431af5b21ac 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -28,6 +28,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Binder.getCallingPid;
@@ -75,6 +76,8 @@ public final class PermissionsUtils {
map.put(DEVICE_PROFILE_GLASSES, Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING);
+ map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING);
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 6729231d68ab..1f3b31692289 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -103,7 +103,8 @@ public class VirtualDeviceManagerService extends SystemService {
private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList(
AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
+ AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
/** Enable default device camera access for apps running on virtual devices. */
@ChangeId
@@ -738,6 +739,11 @@ public class VirtualDeviceManagerService extends SystemService {
public int getDevicePolicy(int deviceId, int policyType) {
return mImpl.getDevicePolicy(deviceId, policyType);
}
+
+ @Override // Binder call
+ public int getDeviceIdForDisplayId(int displayId) {
+ return mImpl.getDeviceIdForDisplayId(displayId);
+ }
}
private final class LocalService extends VirtualDeviceManagerInternal {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ffa259b536ec..06f9e2bf55b2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -127,6 +127,7 @@ java_library_static {
"android.hardware.power-java_shared",
"latest_android_hardware_broadcastradio_java_static",
"services_crashrecovery_stubs_conditionally",
+ "ondeviceintelligence_conditionally",
],
srcs: [
":android.hardware.tv.hdmi.connection-V1-java-source",
@@ -148,6 +149,9 @@ java_library_static {
// Java/AIDL sources to be moved out to CrashRecovery module
":services-crashrecovery-sources",
+
+ // Indicate whether VCN is in platform or mainline
+ ":vcn-location-sources",
],
libs: [
@@ -157,8 +161,8 @@ java_library_static {
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
"android.hardware.vibrator-V3-java",
+ "androidx.annotation_annotation",
"app-compat-annotations",
- "art_exported_aconfig_flags_lib",
"framework-tethering.stubs.module_lib",
"keepanno-annotations",
"service-art.stubs.system_server",
@@ -221,7 +225,6 @@ java_library_static {
"securebox",
"apache-commons-math",
"battery_saver_flag_lib",
- "guava",
"notification_flags_lib",
"power_hint_flags_lib",
"biometrics_flags_lib",
@@ -245,6 +248,7 @@ java_library_static {
"locksettings_flags_lib",
"profiling_flags_lib",
"android.adpf.sessionmanager_aidl-java",
+ "uprobestats_flags_java_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 36dff89d9d61..778c6864282d 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -228,20 +228,31 @@ public class BinaryTransparencyService extends SystemService {
computePackageSignerSha256Digests(packageState.getSigningInfo());
AndroidPackage pkg = packageState.getAndroidPackage();
- for (AndroidPackageSplit split : pkg.getSplits()) {
+ if(pkg != null) {
+ for (AndroidPackageSplit split : pkg.getSplits()) {
+ var appInfo = new IBinaryTransparencyService.AppInfo();
+ appInfo.packageName = packageName;
+ appInfo.longVersion = versionCode;
+ appInfo.splitName = split.getName(); // base's split name is null
+ // Signer digests are consistent between splits, guaranteed by Package Manager.
+ appInfo.signerDigests = signerDigests;
+ appInfo.mbaStatus = mbaStatus;
+
+ // Only digest and split name are different between splits.
+ Digest digest = measureApk(split.getPath());
+ appInfo.digest = digest.value();
+ appInfo.digestAlgorithm = digest.algorithm();
+
+ results.add(appInfo);
+ }
+ } else {
+ Slog.w(TAG, packageName + " APK file is not physically present,"
+ + " skipping split and digest measurement");
var appInfo = new IBinaryTransparencyService.AppInfo();
appInfo.packageName = packageName;
appInfo.longVersion = versionCode;
- appInfo.splitName = split.getName(); // base's split name is null
- // Signer digests are consistent between splits, guaranteed by Package Manager.
appInfo.signerDigests = signerDigests;
appInfo.mbaStatus = mbaStatus;
-
- // Only digest and split name are different between splits.
- Digest digest = measureApk(split.getPath());
- appInfo.digest = digest.value();
- appInfo.digestAlgorithm = digest.algorithm();
-
results.add(appInfo);
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index ccc44a41759b..bedc1308e8eb 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,9 +16,13 @@
package com.android.server;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +37,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -41,10 +46,12 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
@@ -70,7 +77,7 @@ public class GestureLauncherService extends SystemService {
* Time in milliseconds in which the power button must be pressed twice so it will be considered
* as a camera launch.
*/
- @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+ @VisibleForTesting static final long POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
/**
@@ -100,10 +107,23 @@ public class GestureLauncherService extends SystemService {
@VisibleForTesting
static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
- /**
- * Number of taps required to launch camera shortcut.
- */
- private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+ /** Indicates camera should be launched on power double tap. */
+ @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;
+
+ /** Indicates wallet should be launched on power double tap. */
+ @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;
+
+ /** Number of taps required to launch the double tap shortcut (either camera or wallet). */
+ public static final int DOUBLE_POWER_TAP_COUNT_THRESHOLD = 2;
+
+ /** Bundle to send with PendingIntent to grant background activity start privileges. */
+ private static final Bundle GRANT_BACKGROUND_START_PRIVILEGES =
+ ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS)
+ .toBundle();
+
+ private final QuickAccessWalletClient mQuickAccessWalletClient;
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -158,6 +178,9 @@ public class GestureLauncherService extends SystemService {
*/
private boolean mCameraDoubleTapPowerEnabled;
+ /** Whether wallet double tap power button gesture is currently enabled. */
+ private boolean mWalletDoubleTapPowerEnabled;
+
/**
* Whether emergency gesture is currently enabled
*/
@@ -204,15 +227,17 @@ public class GestureLauncherService extends SystemService {
}
}
public GestureLauncherService(Context context) {
- this(context, new MetricsLogger(), new UiEventLoggerImpl());
+ this(context, new MetricsLogger(),
+ QuickAccessWalletClient.create(context), new UiEventLoggerImpl());
}
@VisibleForTesting
GestureLauncherService(Context context, MetricsLogger metricsLogger,
- UiEventLogger uiEventLogger) {
+ QuickAccessWalletClient quickAccessWalletClient, UiEventLogger uiEventLogger) {
super(context);
mContext = context;
mMetricsLogger = metricsLogger;
+ mQuickAccessWalletClient = quickAccessWalletClient;
mUiEventLogger = uiEventLogger;
}
@@ -237,6 +262,9 @@ public class GestureLauncherService extends SystemService {
"GestureLauncherService");
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
@@ -250,12 +278,24 @@ public class GestureLauncherService extends SystemService {
}
private void registerContentObservers() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
- false, mSettingObserver, mUserId);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
- false, mSettingObserver, mUserId);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED),
+ false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE),
+ false, mSettingObserver, mUserId);
+ } else {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ }
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
false, mSettingObserver, mUserId);
@@ -292,6 +332,14 @@ public class GestureLauncherService extends SystemService {
}
@VisibleForTesting
+ void updateWalletDoubleTapPowerEnabled() {
+ boolean enabled = isWalletDoubleTapPowerSettingEnabled(mContext, mUserId);
+ synchronized (this) {
+ mWalletDoubleTapPowerEnabled = enabled;
+ }
+ }
+
+ @VisibleForTesting
void updateEmergencyGestureEnabled() {
boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId);
synchronized (this) {
@@ -418,10 +466,34 @@ public class GestureLauncherService extends SystemService {
Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
}
+
+ /** Checks if camera should be launched on double press of the power button. */
public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
- return isCameraDoubleTapPowerEnabled(context.getResources())
- && (Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+ boolean res;
+
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ res = isDoubleTapPowerGestureSettingEnabled(context, userId)
+ && getDoubleTapPowerGestureAction(context, userId)
+ == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+ } else {
+ // These are legacy settings that will be deprecated once the option to launch both
+ // wallet and camera has been created.
+ res = isCameraDoubleTapPowerEnabled(context.getResources())
+ && (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+ }
+ return res;
+ }
+
+ /** Checks if wallet should be launched on double tap of the power button. */
+ public static boolean isWalletDoubleTapPowerSettingEnabled(Context context, int userId) {
+ if (!launchWalletOptionOnPowerDoubleTap()) {
+ return false;
+ }
+
+ return isDoubleTapPowerGestureSettingEnabled(context, userId)
+ && getDoubleTapPowerGestureAction(context, userId)
+ == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
}
public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) {
@@ -441,6 +513,28 @@ public class GestureLauncherService extends SystemService {
isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
}
+ private static int getDoubleTapPowerGestureAction(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
+ userId);
+ }
+
+ /** Whether the shortcut to launch app on power double press is enabled. */
+ private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
+ userId)
+ == 1;
+ }
+
+ private static boolean isDoubleTapConfigEnabled(Resources resources) {
+ return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
+ }
+
/**
* Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
* value is capped at a maximum
@@ -494,12 +588,19 @@ public class GestureLauncherService extends SystemService {
* Whether GestureLauncherService should be enabled according to system properties.
*/
public static boolean isGestureLauncherEnabled(Resources resources) {
- return isCameraLaunchEnabled(resources)
- || isCameraDoubleTapPowerEnabled(resources)
- || isCameraLiftTriggerEnabled(resources)
- || isEmergencyGestureEnabled(resources);
+ boolean res =
+ isCameraLaunchEnabled(resources)
+ || isCameraLiftTriggerEnabled(resources)
+ || isEmergencyGestureEnabled(resources);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ res |= isDoubleTapConfigEnabled(resources);
+ } else {
+ res |= isCameraDoubleTapPowerEnabled(resources);
+ }
+ return res;
}
+
/**
* Attempts to intercept power key down event by detecting certain gesture patterns
*
@@ -530,6 +631,7 @@ public class GestureLauncherService extends SystemService {
return false;
}
boolean launchCamera = false;
+ boolean launchWallet = false;
boolean launchEmergencyGesture = false;
boolean intercept = false;
long powerTapInterval;
@@ -541,7 +643,7 @@ public class GestureLauncherService extends SystemService {
mFirstPowerDown = event.getEventTime();
mPowerButtonConsecutiveTaps = 1;
mPowerButtonSlowConsecutiveTaps = 1;
- } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) {
// Tap too slow for shortcuts
mFirstPowerDown = event.getEventTime();
mPowerButtonConsecutiveTaps = 1;
@@ -586,10 +688,16 @@ public class GestureLauncherService extends SystemService {
}
}
if (mCameraDoubleTapPowerEnabled
- && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
- && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+ && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
launchCamera = true;
intercept = interactive;
+ } else if (launchWalletOptionOnPowerDoubleTap()
+ && mWalletDoubleTapPowerEnabled
+ && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
+ launchWallet = true;
+ intercept = interactive;
}
}
if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
@@ -608,6 +716,10 @@ public class GestureLauncherService extends SystemService {
(int) powerTapInterval);
mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
}
+ } else if (launchWallet) {
+ Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval="
+ + powerTapInterval + "ms");
+ launchWallet = sendGestureTargetActivityPendingIntent();
} else if (launchEmergencyGesture) {
Slog.i(TAG, "Emergency gesture detected, launching.");
launchEmergencyGesture = handleEmergencyGesture();
@@ -623,11 +735,75 @@ public class GestureLauncherService extends SystemService {
mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launchCamera || launchEmergencyGesture;
+ outLaunched.value = launchCamera || launchEmergencyGesture || launchWallet;
// Intercept power key event if the press is part of a gesture (camera, eGesture) and the
// user has completed setup.
return intercept && isUserSetupComplete();
}
+
+ /**
+ * Fetches and sends gestureTargetActivityPendingIntent from QuickAccessWallet, which is a
+ * specific activity that QuickAccessWalletService has defined to be launch on detection of the
+ * power button gesture.
+ */
+ private boolean sendGestureTargetActivityPendingIntent() {
+ boolean userSetupComplete = isUserSetupComplete();
+ if (mQuickAccessWalletClient == null
+ || !mQuickAccessWalletClient.isWalletServiceAvailable()) {
+ Slog.w(TAG, "QuickAccessWalletService is not available, ignoring wallet gesture.");
+ return false;
+ }
+
+ if (!userSetupComplete) {
+ if (DBG) {
+ Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture.");
+ }
+ return false;
+ }
+ if (DBG) {
+ Slog.d(TAG, "userSetupComplete = true, performing wallet gesture.");
+ }
+
+ mQuickAccessWalletClient.getGestureTargetActivityPendingIntent(
+ getContext().getMainExecutor(),
+ gesturePendingIntent -> {
+ if (gesturePendingIntent == null) {
+ Slog.d(TAG, "getGestureTargetActivityPendingIntent is null.");
+ sendFallbackPendingIntent();
+ return;
+ }
+ sendPendingIntentWithBackgroundStartPrivileges(gesturePendingIntent);
+ });
+ return true;
+ }
+
+ /**
+ * If gestureTargetActivityPendingIntent is null, this method is invoked to start the activity
+ * that QuickAccessWalletService has defined to host the Wallet view, which is typically the
+ * home screen of the Wallet application.
+ */
+ private void sendFallbackPendingIntent() {
+ mQuickAccessWalletClient.getWalletPendingIntent(
+ getContext().getMainExecutor(),
+ walletPendingIntent -> {
+ if (walletPendingIntent == null) {
+ Slog.w(TAG, "getWalletPendingIntent returns null. Not launching "
+ + "anything for wallet.");
+ return;
+ }
+ sendPendingIntentWithBackgroundStartPrivileges(walletPendingIntent);
+ });
+ }
+
+ private void sendPendingIntentWithBackgroundStartPrivileges(PendingIntent pendingIntent) {
+ try {
+ pendingIntent.send(GRANT_BACKGROUND_START_PRIVILEGES);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "PendingIntent was canceled", e);
+ }
+ }
+
+
/**
* @return true if camera was launched, false otherwise.
*/
@@ -709,6 +885,9 @@ public class GestureLauncherService extends SystemService {
registerContentObservers();
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
@@ -720,6 +899,9 @@ public class GestureLauncherService extends SystemService {
if (userId == mUserId) {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 8da835896bd3..96bdbb80ad9b 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -119,6 +119,10 @@
"include-filter": "android.os.storage.cts.StorageStatsManagerTest"
}
]
+ },
+ {
+ "name": "FrameworksMockingServicesTests_service_batteryServiceTest",
+ "file_patterns": ["BatteryService\\.java"]
}
],
"presubmit-large": [
@@ -176,10 +180,6 @@
"include-filter": "com.android.server.wm.BackgroundActivityStart*"
}
]
- },
- {
- "name": "FrameworksMockingServicesTests_service_batteryServiceTest",
- "file_patterns": ["BatteryService\\.java"]
}
]
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e57b00944f7c..bd7a0ac55117 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -449,6 +449,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private AtomicBoolean mIsSatelliteEnabled;
private AtomicBoolean mWasSatelliteEnabledNotified;
+ private final int mPid = Process.myPid();
+
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
* type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -1441,7 +1443,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(r.phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ List<CallState> callList;
+ if (r.callerPid == mPid) {
+ callList = List.copyOf(mCallStateLists.get(r.phoneId));
+ } else {
+ callList = mCallStateLists.get(r.phoneId);
+ }
+ r.callback.onCallStatesChanged(callList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(r.phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -2166,7 +2178,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
overrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
}
boolean isRoaming = telephonyDisplayInfo.isRoaming();
- return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming);
+ boolean isNtn = telephonyDisplayInfo.isNtn();
+ boolean isSatelliteConstrainedData =
+ telephonyDisplayInfo.isSatelliteConstrainedData();
+ return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming,
+ isNtn, isSatelliteConstrainedData);
}
public void notifyCallForwardingChanged(boolean cfi) {
@@ -2569,12 +2585,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (notifyCallState) {
+ List<CallState> copyList = null;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r, subId, phoneId)) {
+ // If listener is in the same process, original instance can be passed
+ // to the listener via AIDL without serialization/de-serialization. We
+ // will pass the copied list. Since the element is newly created instead
+ // of modification for the change, we can use shallow copy for this.
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ if (r.callerPid == mPid && copyList == null) {
+ copyList = List.copyOf(mCallStateLists.get(phoneId));
+ }
+ r.callback.onCallStatesChanged(copyList == null
+ ? mCallStateLists.get(phoneId) : copyList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2902,13 +2931,21 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("There is no active call to report CallQuality");
return;
}
-
+ List<CallState> copyList = null;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r, subId, phoneId)) {
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ if (r.callerPid == mPid && copyList == null) {
+ copyList = List.copyOf(mCallStateLists.get(phoneId));
+ }
+ r.callback.onCallStatesChanged(copyList == null
+ ? mCallStateLists.get(phoneId) : copyList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3ce645158fe4..719928b8e582 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2846,6 +2846,14 @@ public class AccountManagerService
: AccountsDb.DEBUG_ACTION_SET_PASSWORD;
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts,
callingUid);
+
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ TextUtils.isEmpty(password)
+ ? FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__PASSWORD_REMOVED
+ : FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__PASSWORD_CHANGED);
}
} finally {
accounts.accountsDb.endTransaction();
@@ -2912,7 +2920,7 @@ public class AccountManagerService
if (!accountExistsCache(accounts, account)) {
return;
}
- setUserdataInternal(accounts, account, key, value);
+ setUserdataInternal(accounts, account, key, value, callingUid);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -2932,7 +2940,7 @@ public class AccountManagerService
}
private void setUserdataInternal(UserAccounts accounts, Account account, String key,
- String value) {
+ String value, int callingUid) {
synchronized (accounts.dbLock) {
accounts.accountsDb.beginTransaction();
try {
@@ -2958,6 +2966,11 @@ public class AccountManagerService
AccountManager.invalidateLocalAccountUserDataCaches();
}
}
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__USER_DATA_CHANGED);
}
private void onResult(IAccountManagerResponse response, Bundle result) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f41aaeebc49f..cd929c1883d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -361,6 +361,8 @@ import android.os.WorkSource;
import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalMetrics;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
@@ -509,6 +511,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -2781,9 +2784,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mIsolatedAppBindArgs = new ArrayMap<>(1);
// See b/79378449 about the following exemption.
addServiceToMap(mIsolatedAppBindArgs, "package");
- if (!android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
- }
+ addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
}
return mIsolatedAppBindArgs;
}
@@ -2794,38 +2795,27 @@ public class ActivityManagerService extends IActivityManager.Stub
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
-
- // Removing User Service and App Ops Service from cache breaks boot for auto.
- // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
- // TODO: fix SELinux restrictions and remove caching for Android Auto.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- || !android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
- addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
- addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
- addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
- addServiceToMap(mAppBindArgs, "graphicsstats");
- addServiceToMap(mAppBindArgs, "content");
- addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
- addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
- addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
- addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
- addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
- addServiceToMap(mAppBindArgs, "mount");
- addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
- }
- // See b/79378449
- // Getting the window service and package service binder from servicemanager
- // is blocked for Apps. However they are necessary for apps.
- // TODO: remove exception
addServiceToMap(mAppBindArgs, "package");
- addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
- addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
addServiceToMap(mAppBindArgs, "permissionmgr");
+ addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
+ addServiceToMap(mAppBindArgs, "graphicsstats");
addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
+ addServiceToMap(mAppBindArgs, "content");
+ addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
+ addServiceToMap(mAppBindArgs, "mount");
+ addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
}
return mAppBindArgs;
}
@@ -18055,6 +18045,26 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public void getExecutableMethodFileOffsets(@NonNull String processName,
+ int pid, int uid, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback) {
+ final IApplicationThread thread;
+ synchronized (ActivityManagerService.this) {
+ ProcessRecord record = mProcessList.getProcessRecordLocked(processName, uid);
+ if (record == null || record.getPid() != pid) {
+ throw new NoSuchElementException();
+ }
+ thread = record.getThread();
+ }
+ try {
+ thread.getExecutableMethodFileOffsets(methodDescriptor, callback);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "IApplicationThread.getExecutableMethodFileOffsets failed", e);
+ }
+ }
+
+ @Override
public void addCreatorToken(Intent intent, String creatorPackage) {
ActivityManagerService.this.addCreatorToken(intent, creatorPackage);
}
@@ -19334,7 +19344,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!preventIntentRedirect()) return;
if (intent == null) return;
- if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0) {
+ if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
+ && intent.getExtras() != null && intent.getExtras().hasIntent()) {
Slog.wtf(TAG,
"[IntentRedirect] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
@@ -19405,7 +19416,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return createOrGetIntentCreatorToken(intent, key);
} else {
- throw new IllegalArgumentException("intent does not contain a creator token.");
+ return null;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c27126a01a32..aea24d978bee 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1101,6 +1101,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
/** StatsPullAtomCallback for pulling BatteryUsageStats data. */
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+ private static final long BATTERY_USAGE_STATS_PER_UID_MAX_STATS_AGE =
+ TimeUnit.HOURS.toMillis(2);
+
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
final BatteryUsageStats bus;
@@ -1168,7 +1171,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.setMinConsumedPowerThreshold(minConsumedPowerThreshold);
if (isBatteryUsageStatsAccumulationSupported()) {
- query.accumulated();
+ query.accumulated()
+ .setMaxStatsAgeMs(BATTERY_USAGE_STATS_PER_UID_MAX_STATS_AGE);
}
bus = getBatteryUsageStats(List.of(query.build())).get(0);
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 354f281551b2..aa06b7ecf76c 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -316,8 +316,7 @@ class BroadcastController {
return null;
}
if (callerApp.info.uid != SYSTEM_UID
- && !callerApp.getPkgList().containsKey(callerPackage)
- && !"android".equals(callerPackage)) {
+ && !callerApp.getPkgList().containsKey(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 05aeb42dbc9f..83276391493f 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -19,7 +19,6 @@ package com.android.server.am;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.os.Binder;
@@ -41,7 +40,6 @@ public final class BroadcastFilter extends IntentFilter {
* ({@link IntentFilter#SYSTEM_LOW_PRIORITY}, {@link IntentFilter#SYSTEM_HIGH_PRIORITY}).
*/
@ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
static final long RESTRICT_PRIORITY_VALUES = 371309185L;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index a1ab1eea3d3e..8d0805d3fa13 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -43,7 +43,6 @@ import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions.DeliveryGroupPolicy;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -86,7 +85,6 @@ final class BroadcastRecord extends Binder {
* will only influence the order of broadcast delivery within the same process.
*/
@ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
static final long LIMIT_PRIORITY_SCOPE = 371307720L;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f42641ece09b..aadf6f61956c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2840,7 +2840,6 @@ public class OomAdjuster {
return true;
}
}
- capability |= PROCESS_CAPABILITY_CPU_TIME;
}
// Not doing bind OOM management, so treat
// this guy more like a started service.
@@ -3089,7 +3088,6 @@ public class OomAdjuster {
return true;
}
}
- capability |= PROCESS_CAPABILITY_CPU_TIME;
}
}
if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
@@ -4243,6 +4241,11 @@ public class OomAdjuster {
!= client.getSetCapability()) {
// The connection might elevate the importance of the service's capabilities.
needDryRun = true;
+ } else if (Flags.useCpuTimeCapability()
+ && (client.getSetCapability() & ~app.getSetCapability()
+ & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+ // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the service.
+ needDryRun = true;
} else if (Flags.unfreezeBindPolicyFix()
&& cr.hasFlag(Context.BIND_WAIVE_PRIORITY
| Context.BIND_ALLOW_OOM_MANAGEMENT)) {
@@ -4290,6 +4293,10 @@ public class OomAdjuster {
&& client.mOptRecord.shouldNotFreeze()) {
// Process has shouldNotFreeze and it could have gotten it from the client.
return true;
+ } else if (Flags.useCpuTimeCapability()
+ && (client.getSetCapability() & app.getSetCapability()
+ & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+ return true;
}
return false;
}
@@ -4309,6 +4316,11 @@ public class OomAdjuster {
&& client.mOptRecord.shouldNotFreeze()
&& !app.mOptRecord.shouldNotFreeze()) {
needDryRun = true;
+ } else if (Flags.useCpuTimeCapability()
+ && (client.getSetCapability() & ~app.getSetCapability()
+ & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+ // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the provider.
+ needDryRun = true;
}
if (needDryRun) {
@@ -4335,6 +4347,10 @@ public class OomAdjuster {
&& client.mOptRecord.shouldNotFreeze()) {
// Process has shouldNotFreeze and it could have gotten it from the client.
return true;
+ } else if (Flags.useCpuTimeCapability()
+ && (client.getSetCapability() & app.getSetCapability()
+ & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+ return true;
}
return false;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d0153d87ad78..87f87c76725e 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -42,6 +42,7 @@ import android.aconfigd.Aconfigd.StorageReturnMessages;
import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides;
import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately;
+import static com.android.aconfig_new_storage.Flags.enableAconfigdFromMainline;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -142,11 +143,14 @@ public class SettingsToPropertiesMapper {
"tv_os",
"aaos_carframework_triage",
"aaos_performance_triage",
+ "aaos_input_triage",
"aaos_user_triage",
"aaos_window_triage",
"aaos_audio_triage",
"aaos_power_triage",
+ "aaos_storage_triage",
"aaos_sdv",
+ "aaos_vac_triage",
"accessibility",
"android_core_networking",
"android_health_services",
@@ -213,6 +217,7 @@ public class SettingsToPropertiesMapper {
"pixel_bluetooth",
"pixel_connectivity_gps",
"pixel_continuity",
+ "pixel_display",
"pixel_perf",
"pixel_sensors",
"pixel_state_server",
@@ -462,16 +467,38 @@ public class SettingsToPropertiesMapper {
* @param requests: request proto output stream
* @return aconfigd socket return as proto input stream
*/
- static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
+ static void sendAconfigdRequests(ProtoOutputStream requests) {
+ ProtoInputStream returns = sendAconfigdRequests("aconfigd_system", requests);
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
+ if (enableAconfigdFromMainline()) {
+ returns = sendAconfigdRequests("aconfigd_mainline", requests);
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
+ }
+ }
+
+ /**
+ * apply flag local override in aconfig new storage
+ * @param socketName: the socket to send to
+ * @param requests: request proto output stream
+ * @return aconfigd socket return as proto input stream
+ */
+ static ProtoInputStream sendAconfigdRequests(String socketName, ProtoOutputStream requests) {
// connect to aconfigd socket
LocalSocket client = new LocalSocket();
- String socketName = "aconfigd_system";
try {
client.connect(new LocalSocketAddress(
socketName, LocalSocketAddress.Namespace.RESERVED));
- Slog.d(TAG, "connected to aconfigd socket");
+ Slog.d(TAG, "connected to " + socketName + " socket");
} catch (IOException ioe) {
- logErr("failed to connect to aconfigd socket", ioe);
+ logErr("failed to connect to " + socketName + " socket", ioe);
return null;
}
@@ -490,9 +517,9 @@ public class SettingsToPropertiesMapper {
byte[] requests_bytes = requests.getBytes();
outputStream.writeInt(requests_bytes.length);
outputStream.write(requests_bytes, 0, requests_bytes.length);
- Slog.d(TAG, "flag override requests sent to aconfigd");
+ Slog.d(TAG, "flag override requests sent to " + socketName);
} catch (IOException ioe) {
- logErr("failed to send requests to aconfigd", ioe);
+ logErr("failed to send requests to " + socketName, ioe);
return null;
}
@@ -500,10 +527,10 @@ public class SettingsToPropertiesMapper {
try {
int num_bytes = inputStream.readInt();
ProtoInputStream returns = new ProtoInputStream(inputStream);
- Slog.d(TAG, "received " + num_bytes + " bytes back from aconfigd");
+ Slog.d(TAG, "received " + num_bytes + " bytes back from " + socketName);
return returns;
} catch (IOException ioe) {
- logErr("failed to read requests return from aconfigd", ioe);
+ logErr("failed to read requests return from " + socketName, ioe);
return null;
}
}
@@ -644,15 +671,8 @@ public class SettingsToPropertiesMapper {
return;
}
- // send requests to aconfigd and obtain the return byte buffer
- ProtoInputStream returns = sendAconfigdRequests(requests);
-
- // deserialize back using proto input stream
- try {
- parseAndLogAconfigdReturn(returns);
- } catch (IOException ioe) {
- logErr("failed to parse aconfigd return", ioe);
- }
+ // send requests to aconfigd
+ sendAconfigdRequests(requests);
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -762,15 +782,8 @@ public class SettingsToPropertiesMapper {
return;
}
- // send requests to aconfigd and obtain the return
- ProtoInputStream returns = sendAconfigdRequests(requests);
-
- // deserialize back using proto input stream
- try {
- parseAndLogAconfigdReturn(returns);
- } catch (IOException ioe) {
- logErr("failed to parse aconfigd return", ioe);
- }
+ // send requests to aconfigd
+ sendAconfigdRequests(requests);
}
/**
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 6f8dc105850d..c0a97db7275b 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -1423,10 +1423,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
final GameManagerSettings settings = mSettings.get(userId);
// look for the existing GamePackageConfiguration override
- configOverride = settings.getConfigOverride(packageName);
+ configOverride = settings.getConfigOverrideLocked(packageName);
if (configOverride == null) {
configOverride = new GamePackageConfiguration(packageName);
- settings.setConfigOverride(packageName, configOverride);
+ settings.setConfigOverrideLocked(packageName, configOverride);
}
}
GamePackageConfiguration.GameModeConfiguration internalConfig =
@@ -1759,10 +1759,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
final GameManagerSettings settings = mSettings.get(userId);
// look for the existing GamePackageConfiguration override
- configOverride = settings.getConfigOverride(packageName);
+ configOverride = settings.getConfigOverrideLocked(packageName);
if (configOverride == null) {
configOverride = new GamePackageConfiguration(packageName);
- settings.setConfigOverride(packageName, configOverride);
+ settings.setConfigOverrideLocked(packageName, configOverride);
}
}
// modify GameModeConfiguration intervention settings
@@ -1801,7 +1801,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
final GameManagerSettings settings = mSettings.get(userId);
if (gameModeToReset != -1) {
- final GamePackageConfiguration configOverride = settings.getConfigOverride(
+ final GamePackageConfiguration configOverride = settings.getConfigOverrideLocked(
packageName);
if (configOverride == null) {
return;
@@ -1812,10 +1812,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
configOverride.removeModeConfig(gameModeToReset);
if (!configOverride.hasActiveGameModeConfig()) {
- settings.removeConfigOverride(packageName);
+ settings.removeConfigOverrideLocked(packageName);
}
} else {
- settings.removeConfigOverride(packageName);
+ settings.removeConfigOverrideLocked(packageName);
}
}
@@ -2030,7 +2030,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
synchronized (mLock) {
if (mSettings.containsKey(userId)) {
- overrideConfig = mSettings.get(userId).getConfigOverride(packageName);
+ overrideConfig = mSettings.get(userId).getConfigOverrideLocked(packageName);
}
}
if (overrideConfig == null || config == null) {
@@ -2075,7 +2075,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
synchronized (mLock) {
if (mSettings.containsKey(userId)) {
- mSettings.get(userId).removeGame(packageName);
+ mSettings.get(userId).removeGameLocked(packageName);
}
sendUserMessage(userId, WRITE_SETTINGS,
Intent.ACTION_PACKAGE_REMOVED, WRITE_DELAY_MILLIS);
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index b084cf3c3b12..c57a1f73d7d7 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -116,7 +116,7 @@ public class GameManagerSettings {
* Removes all game settings of a given package.
* This operation must be synced with an external lock.
*/
- void removeGame(String packageName) {
+ void removeGameLocked(String packageName) {
mGameModes.remove(packageName);
mConfigOverrides.remove(packageName);
}
@@ -125,7 +125,7 @@ public class GameManagerSettings {
* Returns the game config override of a given package or null if absent.
* This operation must be synced with an external lock.
*/
- GamePackageConfiguration getConfigOverride(String packageName) {
+ GamePackageConfiguration getConfigOverrideLocked(String packageName) {
return mConfigOverrides.get(packageName);
}
@@ -133,7 +133,7 @@ public class GameManagerSettings {
* Sets the game config override of a given package.
* This operation must be synced with an external lock.
*/
- void setConfigOverride(String packageName, GamePackageConfiguration configOverride) {
+ void setConfigOverrideLocked(String packageName, GamePackageConfiguration configOverride) {
mConfigOverrides.put(packageName, configOverride);
}
@@ -141,7 +141,7 @@ public class GameManagerSettings {
* Removes the game mode config override of a given package.
* This operation must be synced with an external lock.
*/
- void removeConfigOverride(String packageName) {
+ void removeConfigOverrideLocked(String packageName) {
mConfigOverrides.remove(packageName);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 295e0443371d..06c586f5e9c2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3191,7 +3191,7 @@ public class AppOpsService extends IAppOpsService.Stub {
resolveProxyPackageName, proxyAttributionTag, proxyVirtualDeviceId,
Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, proxyFlags, !isProxyTrusted,
- "proxy " + message, shouldCollectMessage, 1);
+ "proxy " + message, shouldCollectMessage);
if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
proxiedPackageName);
@@ -3210,20 +3210,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
proxiedAttributionTag, proxiedVirtualDeviceId, proxyUid, resolveProxyPackageName,
proxyAttributionTag, proxyVirtualDeviceId, proxiedFlags, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, 1);
- }
-
- @Override
- public void noteOperationsInBatch(Map batchedNoteOps) {
- for (var entry : ((Map<AppOpsManager.NotedOp, Integer>) batchedNoteOps).entrySet()) {
- AppOpsManager.NotedOp notedOp = entry.getKey();
- int notedCount = entry.getValue();
- mCheckOpsDelegateDispatcher.noteOperation(
- notedOp.getOp(), notedOp.getUid(), notedOp.getPackageName(),
- notedOp.getAttributionTag(), notedOp.getVirtualDeviceId(),
- notedOp.getShouldCollectAsyncNotedOp(), notedOp.getMessage(),
- notedOp.getShouldCollectMessage(), notedCount);
- }
+ message, shouldCollectMessage);
}
@Override
@@ -3241,7 +3228,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, 1);
+ shouldCollectMessage);
}
@Override
@@ -3250,12 +3237,13 @@ public class AppOpsService extends IAppOpsService.Stub {
String message, boolean shouldCollectMessage) {
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, 1);
+ shouldCollectMessage);
}
private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount) {
+ @Nullable String attributionTag, int virtualDeviceId,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage) {
String resolvedPackageName;
if (!shouldUseNewCheckOp()) {
verifyIncomingUid(uid);
@@ -3290,14 +3278,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
virtualDeviceId, Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, notedCount);
+ message, shouldCollectMessage);
}
private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, int proxyUid,
String proxyPackageName, @Nullable String proxyAttributionTag, int proxyVirtualDeviceId,
@OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3400,11 +3388,11 @@ public class AppOpsService extends IAppOpsService.Stub {
virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
+ getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags);
if (shouldCollectAsyncNotedOp) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
- shouldCollectMessage, notedCount);
+ shouldCollectMessage);
}
return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
@@ -3563,7 +3551,7 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
@Nullable String attributionTag, @OpFlags int flags, @NonNull String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
Objects.requireNonNull(message);
int callingUid = Binder.getCallingUid();
@@ -3571,51 +3559,42 @@ public class AppOpsService extends IAppOpsService.Stub {
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
+ Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
+
+ RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
+ AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
+ attributionTag, message, System.currentTimeMillis());
+ final boolean[] wasNoteForwarded = {false};
+
if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) != 0
&& shouldCollectMessage) {
reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode,
attributionTag, message);
}
- Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
- RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
- if (callbacks == null) {
- return;
- }
-
- final boolean[] wasNoteForwarded = {false};
- if (Flags.rateLimitBatchedNoteOpAsyncCallbacksEnabled()) {
- notedCount = 1;
- }
-
- for (int i = 0; i < notedCount; i++) {
- AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
- attributionTag, message, System.currentTimeMillis());
- wasNoteForwarded[0] = false;
+ if (callbacks != null) {
callbacks.broadcast((cb) -> {
try {
cb.opNoted(asyncNotedOp);
wasNoteForwarded[0] = true;
} catch (RemoteException e) {
Slog.e(TAG,
- "Could not forward noteOp of " + opCode + " to "
- + packageName
+ "Could not forward noteOp of " + opCode + " to " + packageName
+ "/" + uid + "(" + attributionTag + ")", e);
}
});
+ }
- if (!wasNoteForwarded[0]) {
- ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(
- key);
- if (unforwardedOps == null) {
- unforwardedOps = new ArrayList<>(1);
- mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
- }
+ if (!wasNoteForwarded[0]) {
+ ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(key);
+ if (unforwardedOps == null) {
+ unforwardedOps = new ArrayList<>(1);
+ mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
+ }
- unforwardedOps.add(asyncNotedOp);
- if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
- unforwardedOps.remove(0);
- }
+ unforwardedOps.add(asyncNotedOp);
+ if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ unforwardedOps.remove(0);
}
}
}
@@ -4047,7 +4026,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (shouldCollectAsyncNotedOp && !isRestricted) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
- message, shouldCollectMessage, 1);
+ message, shouldCollectMessage);
}
return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
@@ -7595,36 +7574,34 @@ public class AppOpsService extends IAppOpsService.Stub {
public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- String message, boolean shouldCollectMessage, int notedCount) {
+ String message, boolean shouldCollectMessage) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, notedCount, this::noteDelegateOperationImpl
+ shouldCollectMessage, this::noteDelegateOperationImpl
);
} else {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, notedCount, AppOpsService.this::noteOperationImpl
+ shouldCollectMessage, AppOpsService.this::noteOperationImpl
);
}
} else if (mCheckOpsDelegate != null) {
return noteDelegateOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
return noteOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
@Nullable String packageName, @Nullable String featureId, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount, AppOpsService.this::noteOperationImpl
+ AppOpsService.this::noteOperationImpl
);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index fa2e674d37c7..ca9a25b86aed 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -28,6 +28,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
+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;
@@ -176,6 +177,8 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
case OP_RECORD_AUDIO:
case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ case OP_CONTROL_AUDIO:
+ return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
default:
return PROCESS_CAPABILITY_NONE;
}
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 4d114b4ad4ac..314664b0a79d 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -100,12 +100,10 @@ final class AttributedOp {
* @param proxyDeviceId The device Id of the proxy
* @param uidState UID state of the app noteOp/startOp was called for
* @param flags OpFlags of the call
- * @param accessCount The number of times the op is accessed
*/
public void accessed(int proxyUid, @Nullable String proxyPackageName,
@Nullable String proxyAttributionTag, @Nullable String proxyDeviceId,
- @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
- int accessCount) {
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
long accessTime = System.currentTimeMillis();
accessed(accessTime, -1, proxyUid, proxyPackageName, proxyAttributionTag, proxyDeviceId,
uidState, flags);
@@ -113,7 +111,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
- DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+ DiscreteRegistry.ACCESS_TYPE_NOTE_OP);
}
/**
@@ -257,7 +255,7 @@ final class AttributedOp {
if (isStarted) {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
+ attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP);
}
}
@@ -453,7 +451,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), startTime, event.getAttributionFlags(),
- event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
+ event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP);
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5e67f26ba1f6..6b0253864e2b 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -475,7 +475,7 @@ final class HistoricalRegistry {
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long accessTime,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteRegistry.AccessType int accessType, int accessCount) {
+ @DiscreteRegistry.AccessType int accessType) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -484,7 +484,7 @@ final class HistoricalRegistry {
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
- attributionTag, uidState, flags, accessCount);
+ attributionTag, uidState, flags, 1);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
attributionTag, flags, uidState, accessTime, -1, attributionFlags,
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 34d4fb02ad99..acb46d9b85e6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -793,6 +793,7 @@ public class AudioDeviceInventory {
* (see AudioService.onAudioServerDied() method)
*/
// Always executed on AudioDeviceBroker message queue
+ @GuardedBy("mDeviceBroker.mDeviceStateLock")
/*package*/ void onRestoreDevices() {
synchronized (mDevicesLock) {
int res;
@@ -815,6 +816,9 @@ public class AudioDeviceInventory {
"Device inventory restore failed to reconnect " + di,
EventLogger.Event.ALOGE, TAG);
mConnectedDevices.remove(di.getKey(), di);
+ if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
+ mDeviceBroker.onSetBtScoActiveDevice(null);
+ }
}
}
mAppliedStrategyRolesInt.clear();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2f7a54daed27..1799b7715e5c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@ import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IUidObserver;
import android.app.NotificationManager;
+import android.app.PropertyInvalidatedCache;
import android.app.UidObserver;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -248,6 +249,7 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -4688,6 +4690,12 @@ public class AudioService extends IAudioService.Stub
switch (mode) {
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
+ // TODO(b/382704431): remove to allow STREAM_VOICE_CALL to drive abs volume
+ // over A2DP
+ if (getDeviceForStream(AudioSystem.STREAM_VOICE_CALL)
+ == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ return AudioSystem.STREAM_MUSIC;
+ }
return AudioSystem.STREAM_VOICE_CALL;
case AudioSystem.MODE_CALL_SCREENING:
case AudioSystem.MODE_COMMUNICATION_REDIRECT:
@@ -4699,15 +4707,20 @@ public class AudioService extends IAudioService.Stub
// other conditions will influence the stream type choice, read on...
break;
}
- if (voiceActivityCanOverride
- && mVoicePlaybackActive.get()) {
+
+ if (voiceActivityCanOverride && mVoicePlaybackActive.get()) {
+ // TODO(b/382704431): remove to allow STREAM_VOICE_CALL to drive abs volume over A2DP
+ if (getDeviceForStream(AudioSystem.STREAM_VOICE_CALL)
+ == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ return AudioSystem.STREAM_MUSIC;
+ }
return AudioSystem.STREAM_VOICE_CALL;
}
return AudioSystem.STREAM_MUSIC;
}
- private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
- private AtomicBoolean mMediaPlaybackActive = new AtomicBoolean(false);
+ private final AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
+ private final AtomicBoolean mMediaPlaybackActive = new AtomicBoolean(false);
private final IPlaybackConfigDispatcher mPlaybackActivityMonitor =
new IPlaybackConfigDispatcher.Stub() {
@@ -4927,7 +4940,7 @@ public class AudioService extends IAudioService.Stub
private void onUpdateContextualVolumes() {
final int streamType = getBluetoothContextualVolumeStream();
- Log.i(TAG,
+ Slog.i(TAG,
"onUpdateContextualVolumes: absolute volume driving streams " + streamType
+ " avrcp supported: " + mAvrcpAbsVolSupported);
synchronized (mCachedAbsVolDrivingStreamsLock) {
@@ -4936,7 +4949,7 @@ public class AudioService extends IAudioService.Stub
if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
if (!enabled) {
- Log.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
+ Slog.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
}
}
if (stream != streamType) {
@@ -4964,7 +4977,7 @@ public class AudioService extends IAudioService.Stub
return;
}
if (absVolumeDevices.size() > 1) {
- Log.w(TAG, "onUpdateContextualVolumes too many active devices: "
+ Slog.w(TAG, "onUpdateContextualVolumes too many active devices: "
+ absVolumeDevices.stream().map(AudioSystem::getOutputDeviceName)
.collect(Collectors.joining(","))
+ ", for stream: " + streamType);
@@ -4975,7 +4988,7 @@ public class AudioService extends IAudioService.Stub
final int index = getStreamVolume(streamType, device);
if (DEBUG_VOL) {
- Log.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
+ Slog.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
+ ", device: " + AudioSystem.getOutputDeviceName(device)
+ ", index: " + index);
}
@@ -10941,14 +10954,122 @@ public class AudioService extends IAudioService.Stub
}
};
mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
task);
} else {
mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
() -> mAudioServerLifecycleExecutor.execute(
mPermissionProvider::onPermissionStateChanged));
}
+
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ new PackageInfoTransducer().start();
+ }
+ }
+
+ /**
+ * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
+ * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
+ * property. This operates on the popcorn principle: changes in the source are done when the
+ * source has been quiet for the soak interval.
+ *
+ * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
+ * sniffing. It should be cleaned up.
+ */
+ private static class PackageInfoTransducer extends Thread {
+
+ // The run/stop signal.
+ private final AtomicBoolean mRunning = new AtomicBoolean(false);
+
+ // The source of change information.
+ private final PropertyInvalidatedCache.NonceWatcher mWatcher;
+
+ // The handler for scheduling delayed reactions to changes.
+ private final Handler mHandler;
+
+ // How long to soak changes: 50ms is the legacy choice.
+ private final static long SOAK_TIME_MS = 50;
+
+ // The ubiquitous lock.
+ private final Object mLock = new Object();
+
+ // If positive, this is the soak expiration time.
+ @GuardedBy("mLock")
+ private long mSoakDeadlineMs = -1;
+
+ // A source of unique long values.
+ @GuardedBy("mLock")
+ private long mToken = 0;
+
+ PackageInfoTransducer() {
+ mWatcher = PropertyInvalidatedCache
+ .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+ mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ PackageInfoTransducer.this.handleMessage(msg);
+ }};
+ }
+
+ public void run() {
+ mRunning.set(true);
+ while (mRunning.get()) {
+ try {
+ final int changes = mWatcher.waitForChange();
+ if (changes == 0 || !mRunning.get()) {
+ continue;
+ }
+ } catch (InterruptedException e) {
+ // We don't know why the exception occurred but keep running until told to
+ // stop.
+ continue;
+ }
+ trigger();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateLocked() {
+ String n = Long.toString(mToken++);
+ SystemProperties.set(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
+ }
+
+ private void trigger() {
+ synchronized (mLock) {
+ boolean alreadyQueued = mSoakDeadlineMs >= 0;
+ final long nowMs = SystemClock.uptimeMillis();
+ mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
+ if (!alreadyQueued) {
+ mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
+ updateLocked();
+ }
+ }
+ }
+
+ private void handleMessage(Message msg) {
+ synchronized (mLock) {
+ if (mSoakDeadlineMs < 0) {
+ return; // ???
+ }
+ final long nowMs = SystemClock.uptimeMillis();
+ if (mSoakDeadlineMs > nowMs) {
+ mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
+ mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
+ return;
+ }
+ mSoakDeadlineMs = -1;
+ updateLocked();
+ }
+ }
+
+ /**
+ * Cause the thread to exit. Running is set to false and the watcher is awakened.
+ */
+ public void done() {
+ mRunning.set(false);
+ mWatcher.wakeUp();
+ }
}
//==========================================================================================
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6feae34f1a2d..8b6b0f9419a0 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -523,7 +523,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
// older target sdk to impact all system uid apps
if (Flags.systemUidTargetSystemSdk() && !mIsWear &&
uid == Process.SYSTEM_UID && appInfo != null) {
- appInfo.targetSdkVersion = Build.VERSION.SDK_INT;
+ appInfo.targetSdkVersion = mBuildClassifier.platformTargetSdk();
}
return appInfo;
}
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index fc20ef20f63d..7154bc47fc33 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -73,6 +73,8 @@ public class SyncLogger {
*/
public static synchronized SyncLogger getInstance() {
if (sInstance == null) {
+ // Always default to the sync logger for now (see b/381957278#comment8).
+ /*
final String flag = SystemProperties.get("debug.synclog");
final boolean enable =
(Build.IS_DEBUGGABLE
@@ -83,6 +85,8 @@ public class SyncLogger {
} else {
sInstance = new SyncLogger();
}
+ */
+ sInstance = new SyncLogger();
}
return sInstance;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 251344395ae3..5d9db65fe2b2 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,17 +19,19 @@ package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
@@ -98,7 +100,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
@@ -854,7 +855,7 @@ public final class DeviceStateManagerService extends SystemService {
}
}
- private void requestStateInternal(int state, int flags, int callingPid, int callingUid,
+ private void requestStateInternal(int requestedState, int flags, int callingPid, int callingUid,
@NonNull IBinder token, boolean hasControlDeviceStatePermission) {
synchronized (mLock) {
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
@@ -869,19 +870,30 @@ public final class DeviceStateManagerService extends SystemService {
+ " token: " + token);
}
- final Optional<DeviceState> deviceState = getStateLocked(state);
- if (!deviceState.isPresent()) {
- throw new IllegalArgumentException("Requested state: " + state
+ final Optional<DeviceState> requestedDeviceState = getStateLocked(requestedState);
+ if (requestedDeviceState.isEmpty()) {
+ throw new IllegalArgumentException("Requested state: " + requestedState
+ " is not supported.");
}
- OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
- deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+ final OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
+ requestedDeviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
if (Flags.deviceStatePropertyMigration()) {
- // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
- if (!hasControlDeviceStatePermission && deviceState.get().hasProperty(
- PROPERTY_FEATURE_REAR_DISPLAY)) {
+ final boolean isRequestingRdm = requestedDeviceState.get()
+ .hasProperty(PROPERTY_FEATURE_REAR_DISPLAY);
+ final boolean isRequestingRdmOuterDefault = requestedDeviceState.get()
+ .hasProperty(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+
+ final boolean isDeviceClosed = mCommittedState.isEmpty() ? false
+ : mCommittedState.get().hasProperty(
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+
+ final boolean shouldShowRdmEduDialog = isRequestingRdm && shouldShowRdmEduDialog(
+ hasControlDeviceStatePermission, isRequestingRdmOuterDefault,
+ isDeviceClosed);
+
+ if (shouldShowRdmEduDialog) {
showRearDisplayEducationalOverlayLocked(request);
} else {
mOverrideRequestController.addRequest(request);
@@ -889,7 +901,7 @@ public final class DeviceStateManagerService extends SystemService {
} else {
// If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
if (!hasControlDeviceStatePermission && mRearDisplayState != null
- && state == mRearDisplayState.getIdentifier()) {
+ && requestedState == mRearDisplayState.getIdentifier()) {
showRearDisplayEducationalOverlayLocked(request);
} else {
mOverrideRequestController.addRequest(request);
@@ -899,6 +911,28 @@ public final class DeviceStateManagerService extends SystemService {
}
/**
+ * Determines if the system should show an educational dialog before entering rear display mode
+ * @param hasControlDeviceStatePermission If the app has the CONTROL_DEVICE_STATE permission, we
+ * don't need to show the overlay
+ * @param requestingRdmOuterDefault True if the system is requesting
+ * PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
+ * @param isDeviceClosed True if the device is closed (folded) when the request was made
+ */
+ @VisibleForTesting
+ static boolean shouldShowRdmEduDialog(boolean hasControlDeviceStatePermission,
+ boolean requestingRdmOuterDefault, boolean isDeviceClosed) {
+ if (hasControlDeviceStatePermission) {
+ return false;
+ }
+
+ if (requestingRdmOuterDefault) {
+ return isDeviceClosed;
+ } else {
+ return true;
+ }
+ }
+
+ /**
* If we get a request to enter rear display mode, we need to display an educational
* overlay to let the user know what will happen. This calls into the
* {@link StatusBarManagerInternal} to notify SystemUI to display the educational dialog.
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 83b0801ce87f..50d650855b05 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -37,7 +37,6 @@ class BrightnessRangeController {
private final HdrClamper mHdrClamper;
private final Runnable mModeChangeCallback;
- private final boolean mUseNbmController;
private final boolean mUseHdrClamper;
@@ -62,11 +61,8 @@ class BrightnessRangeController {
mHdrClamper = hdrClamper;
mNormalBrightnessModeController = normalBrightnessModeController;
mUseHdrClamper = flags.isHdrClamperEnabled() && !flags.useNewHdrBrightnessModifier();
- mUseNbmController = flags.isNbmControllerEnabled();
- if (mUseNbmController) {
- mNormalBrightnessModeController.resetNbmData(
- displayDeviceConfig.getLuxThrottlingData());
- }
+ mNormalBrightnessModeController.resetNbmData(
+ displayDeviceConfig.getLuxThrottlingData());
if (flags.useNewHdrBrightnessModifier()) {
// HDR boost is handled by HdrBrightnessModifier and should be disabled in HbmController
mHbmController.disableHdrBoost();
@@ -76,7 +72,6 @@ class BrightnessRangeController {
void dump(PrintWriter pw) {
pw.println("BrightnessRangeController:");
- pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
pw.println(" mUseHdrClamper=" + mUseHdrClamper);
mHbmController.dump(pw);
mNormalBrightnessModeController.dump(pw);
@@ -138,9 +133,7 @@ class BrightnessRangeController {
float getCurrentBrightnessMax() {
// nbmController might adjust maxBrightness only if device does not support HBM or
// hbm is currently not allowed
- if (mUseNbmController
- && (!mHbmController.deviceSupportsHbm()
- || !mHbmController.isHbmCurrentlyAllowed())) {
+ if (!mHbmController.deviceSupportsHbm() || !mHbmController.isHbmCurrentlyAllowed()) {
return Math.min(mHbmController.getCurrentBrightnessMax(),
mNormalBrightnessModeController.getCurrentBrightnessMax());
}
@@ -173,16 +166,12 @@ class BrightnessRangeController {
}
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
- if (mUseNbmController) {
- boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
- hbmChangesFunc.run();
- // if nbm transition changed - trigger callback
- // HighBrightnessModeController handles sending changes itself
- if (nbmTransitionChanged) {
- mModeChangeCallback.run();
- }
- } else {
- hbmChangesFunc.run();
+ boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
+ hbmChangesFunc.run();
+ // if nbm transition changed - trigger callback
+ // HighBrightnessModeController handles sending changes itself
+ if (nbmTransitionChanged) {
+ mModeChangeCallback.run();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 4bbddaeb53a2..2bdb5c25d0d5 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -306,6 +306,14 @@ abstract class DisplayDevice {
}
/**
+ * Returns if the display should only mirror another display rather than showing other content
+ * until it is destroyed.
+ */
+ public boolean shouldOnlyMirror() {
+ return false;
+ }
+
+ /**
* Sets the display layer stack while in a transaction.
*/
public final void setLayerStackLocked(SurfaceControl.Transaction t, int layerStack,
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5c6299559856..bad5b8b9567a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -47,6 +47,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
+import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.provider.Settings.Secure.RESOLUTION_MODE_FULL;
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
@@ -71,6 +72,7 @@ import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -197,7 +199,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
-
/**
* Manages attached displays.
* <p>
@@ -534,6 +535,8 @@ public final class DisplayManagerService extends SystemService {
private final boolean mExtraDisplayEventLogging;
private final String mExtraDisplayLoggingPackageName;
+ private boolean mMirrorBuiltInDisplay;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -782,7 +785,11 @@ public final class DisplayManagerService extends SystemService {
}
dpc.onSwitchUser(newUserId, userSerial, newBrightness);
});
- handleSettingsChange();
+ handleMinimalPostProcessingAllowedSettingChange();
+
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
}
}
@@ -825,12 +832,15 @@ public final class DisplayManagerService extends SystemService {
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
- updateSettingsLocked();
+ updateMinimalPostProcessingAllowedSettingLocked();
updateUserDisabledHdrTypesFromSettingsLocked();
updateUserPreferredDisplayModeSettingsLocked();
if (mIsHdrOutputControlEnabled) {
updateHdrConversionModeSettingsLocked();
}
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
}
mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
@@ -895,6 +905,16 @@ public final class DisplayManagerService extends SystemService {
}
}
+ @VisibleForTesting
+ ContentObserver getSettingsObserver() {
+ return mSettingsObserver;
+ }
+
+ @VisibleForTesting
+ boolean shouldMirrorBuiltInDisplay() {
+ return mMirrorBuiltInDisplay;
+ }
+
DisplayNotificationManager getDisplayNotificationManager() {
return mDisplayNotificationManager;
}
@@ -1180,27 +1200,59 @@ public final class DisplayManagerService extends SystemService {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED), false, this);
+
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ MIRROR_BUILT_IN_DISPLAY), false, this, UserHandle.USER_ALL);
+ }
}
@Override
public void onChange(boolean selfChange, Uri uri) {
- handleSettingsChange();
+ if (Settings.Secure.getUriFor(
+ Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED).equals(uri)) {
+ handleMinimalPostProcessingAllowedSettingChange();
+ return;
+ }
+
+ if (Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY).equals(uri)) {
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
+ return;
+ }
}
}
- private void handleSettingsChange() {
+ private void handleMinimalPostProcessingAllowedSettingChange() {
synchronized (mSyncRoot) {
- updateSettingsLocked();
+ updateMinimalPostProcessingAllowedSettingLocked();
scheduleTraversalLocked(false);
}
}
- private void updateSettingsLocked() {
+ private void updateMinimalPostProcessingAllowedSettingLocked() {
setMinimalPostProcessingAllowed(Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
1, UserHandle.USER_CURRENT) != 0);
}
+ private void updateMirrorBuiltInDisplaySettingLocked() {
+ synchronized (mSyncRoot) {
+ ContentResolver resolver = mContext.getContentResolver();
+ final boolean mirrorBuiltInDisplay = Settings.Secure.getIntForUser(resolver,
+ MIRROR_BUILT_IN_DISPLAY, 0, UserHandle.USER_CURRENT) != 0;
+ if (mMirrorBuiltInDisplay == mirrorBuiltInDisplay) {
+ return;
+ }
+ mMirrorBuiltInDisplay = mirrorBuiltInDisplay;
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ mLogicalDisplayMapper.forEachLocked(this::updateCanHostTasksIfNeededLocked);
+ }
+ }
+ }
+
private void restoreResolutionFromBackup() {
int savedMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.SCREEN_RESOLUTION_MODE,
@@ -1997,7 +2049,7 @@ public final class DisplayManagerService extends SystemService {
// handles stopping the projection.
Slog.w(TAG, "Content Recording: failed to start mirroring - "
+ "releasing virtual display " + displayId);
- releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
+ releaseVirtualDisplayInternal(callback.asBinder());
return Display.INVALID_DISPLAY;
} else if (projection != null) {
// Indicate that this projection has been used to record, and can't be used
@@ -2086,7 +2138,7 @@ public final class DisplayManagerService extends SystemService {
// Something weird happened and the logical display was not created.
Slog.w(TAG, "Rejecting request to create virtual display "
+ "because the logical display was not created.");
- mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder(), callingUid);
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
return -1;
@@ -2113,14 +2165,14 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void releaseVirtualDisplayInternal(IBinder appToken, int callingUid) {
+ private void releaseVirtualDisplayInternal(IBinder appToken) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
DisplayDevice device =
- mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken, callingUid);
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
Slog.d(TAG, "Virtual Display: Display Device released");
if (device != null) {
// TODO: multi-display - handle virtual displays the same as other display adapters.
@@ -2263,6 +2315,10 @@ public final class DisplayManagerService extends SystemService {
mDisplayBrightnesses.append(displayId,
new BrightnessPair(brightnessDefault, brightnessDefault));
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateCanHostTasksIfNeededLocked(display);
+ }
+
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
}
@@ -2585,6 +2641,12 @@ public final class DisplayManagerService extends SystemService {
}
}
+ private void updateCanHostTasksIfNeededLocked(LogicalDisplay display) {
+ if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay)) {
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ }
+ }
+
private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
// We must only persist the inset after boot has completed, otherwise we will end up
// overwriting the persisted value before the masking flag has been loaded from the
@@ -4744,10 +4806,9 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void releaseVirtualDisplay(IVirtualDisplayCallback callback) {
- final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
+ releaseVirtualDisplayInternal(callback.asBinder());
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 945365dcf8fe..730b95cf1eac 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -906,6 +906,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mLogicalDisplay.getPowerThrottlingDataIdLocked();
mHandler.postAtTime(() -> {
+ if (mStopped) {
+ // DPC has already stopped, don't execute any more.
+ return;
+ }
+
boolean changed = false;
if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
@@ -1641,6 +1646,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
(mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY)
|| mAutomaticBrightnessStrategy
.isTemporaryAutoBrightnessAdjustmentApplied();
+ float rampSpeed = 0;
if (!mPendingScreenOff) {
if (mSkipScreenOnBrightnessRamp) {
if (state == Display.STATE_ON) {
@@ -1742,7 +1748,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
customAnimationRate, /* ignoreAnimationLimits = */true);
} else {
boolean isIncreasing = animateValue > currentBrightness;
- final float rampSpeed;
final boolean idle = mAutomaticBrightnessController != null
&& mAutomaticBrightnessController.isInIdleMode();
if (isIncreasing && slowChange) {
@@ -1827,6 +1832,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
.getDisplayBrightnessStrategyName());
mTempBrightnessEvent.setAutomaticBrightnessEnabled(
displayBrightnessState.getShouldUseAutoBrightness());
+ mTempBrightnessEvent.setSlowChange(slowChange);
+ mTempBrightnessEvent.setRampSpeed(rampSpeed);
// Temporary is what we use during slider interactions. We avoid logging those so that
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
@@ -2228,7 +2235,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
blockScreenOff();
mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
- unblockScreenOff();
} else if (mPendingScreenOffUnblocker != null) {
// Abort doing the state change until screen off is unblocked.
return false;
@@ -3306,7 +3312,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
int displayId, SensorManager sensorManager) {
return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
looper, nudgeUpdatePowerState,
- displayId, sensorManager, /* injector= */ null);
+ displayId, sensorManager);
}
AutomaticBrightnessController getAutomaticBrightnessController(
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index 215932ca19be..35455c841c7b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -34,6 +36,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Maintains the proximity state of the display.
@@ -42,18 +46,26 @@ import java.io.PrintWriter;
*/
public final class DisplayPowerProximityStateController {
@VisibleForTesting
- static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
- @VisibleForTesting
static final int PROXIMITY_UNKNOWN = -1;
+ private static final int PROXIMITY_NEGATIVE = 0;
@VisibleForTesting
static final int PROXIMITY_POSITIVE = 1;
+
+ @IntDef(prefix = { "PROXIMITY_" }, value = {
+ PROXIMITY_UNKNOWN,
+ PROXIMITY_NEGATIVE,
+ PROXIMITY_POSITIVE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ProximityState {}
+
+ @VisibleForTesting
+ static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
@VisibleForTesting
static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
private static final int MSG_IGNORE_PROXIMITY = 2;
- private static final int PROXIMITY_NEGATIVE = 0;
-
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
// Proximity sensor debounce delay in milliseconds for positive transitions.
@@ -73,7 +85,7 @@ public final class DisplayPowerProximityStateController {
private final DisplayPowerProximityStateHandler mHandler;
// A runnable to execute the utility to update the power state.
private final Runnable mNudgeUpdatePowerState;
- private Clock mClock;
+ private final Clock mClock;
// A listener which listen's to the events emitted by the proximity sensor.
private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
@Override
@@ -117,9 +129,6 @@ public final class DisplayPowerProximityStateController {
// with the sensor manager.
private boolean mProximitySensorEnabled;
- // The raw non-debounced proximity sensor state.
- private int mPendingProximity = PROXIMITY_UNKNOWN;
-
// -1 if fully debounced. Else, represents the time in ms when the debounce suspend blocker will
// be removed. Applies for both positive and negative proximity flips.
private long mPendingProximityDebounceTime = -1;
@@ -128,8 +137,11 @@ public final class DisplayPowerProximityStateController {
// When the screen turns on again, we report user activity to the power manager.
private boolean mScreenOffBecauseOfProximity;
+ // The raw non-debounced proximity sensor state.
+ private @ProximityState int mPendingProximity = PROXIMITY_UNKNOWN;
+
// The debounced proximity sensor state.
- private int mProximity = PROXIMITY_UNKNOWN;
+ private @ProximityState int mProximity = PROXIMITY_UNKNOWN;
// The actual proximity sensor threshold value.
private float mProximityThreshold;
@@ -139,7 +151,7 @@ public final class DisplayPowerProximityStateController {
private boolean mSkipRampBecauseOfProximityChangeToNegative = false;
// The DisplayId of the associated Logical Display.
- private int mDisplayId;
+ private final int mDisplayId;
/**
* Create a new instance of DisplayPowerProximityStateController.
@@ -152,11 +164,18 @@ public final class DisplayPowerProximityStateController {
* @param displayId The DisplayId of the associated Logical Display.
* @param sensorManager The manager which lets us access the display's ProximitySensor
*/
- public DisplayPowerProximityStateController(
- WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig,
- Looper looper,
+ public DisplayPowerProximityStateController(WakelockController wakeLockController,
+ DisplayDeviceConfig displayDeviceConfig, Looper looper,
+ Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager) {
+ this(wakeLockController, displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
+ sensorManager, new Injector());
+ }
+
+ @VisibleForTesting
+ DisplayPowerProximityStateController(WakelockController wakeLockController,
+ DisplayDeviceConfig displayDeviceConfig, Looper looper,
Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager,
- Injector injector) {
+ @Nullable Injector injector) {
if (injector == null) {
injector = new Injector();
}
@@ -437,7 +456,7 @@ public final class DisplayPowerProximityStateController {
if (mProximity != mPendingProximity) {
// if the status of the sensor changed, stop ignoring.
mIgnoreProximityUntilChanged = false;
- Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
+ Slog.i(mTag, "Applying proximity: " + proximityToString(mPendingProximity));
}
// Sensor reading accepted. Apply the change then release the wake lock.
mProximity = mPendingProximity;
@@ -478,7 +497,7 @@ public final class DisplayPowerProximityStateController {
}
}
- private String proximityToString(int state) {
+ private String proximityToString(@ProximityState int state) {
switch (state) {
case PROXIMITY_UNKNOWN:
return "Unknown";
@@ -518,12 +537,12 @@ public final class DisplayPowerProximityStateController {
}
@VisibleForTesting
- int getPendingProximity() {
+ @ProximityState int getPendingProximity() {
return mPendingProximity;
}
@VisibleForTesting
- int getProximity() {
+ @ProximityState int getProximity() {
return mProximity;
}
@@ -550,7 +569,7 @@ public final class DisplayPowerProximityStateController {
@VisibleForTesting
static class Injector {
Clock createClock() {
- return () -> SystemClock.uptimeMillis();
+ return SystemClock::uptimeMillis;
}
}
}
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index f34d2cc6e684..519763a1c3db 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -217,8 +217,10 @@ class ExternalDisplayPolicy {
mExternalDisplayStatsService.onDisplayConnected(logicalDisplay);
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
+ if (((Build.IS_ENG || Build.IS_USERDEBUG)
+ && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false))
+ || (mFlags.isDisplayContentModeManagementEnabled()
+ && logicalDisplay.canHostTasksLocked())) {
Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED);
return;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 1de9c9589fb9..f9d413732e3e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
@@ -227,6 +228,8 @@ final class LogicalDisplay {
*/
private final boolean mIsAnisotropyCorrectionEnabled;
+ private boolean mCanHostTasks;
+
LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
this(displayId, layerStack, primaryDisplayDevice, false, false);
}
@@ -245,6 +248,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
mIsAnisotropyCorrectionEnabled = isAnisotropyCorrectionEnabled;
mAlwaysRotateDisplayDeviceEnabled = isAlwaysRotateDisplayDeviceEnabled;
+ mCanHostTasks = (mDisplayId == Display.DEFAULT_DISPLAY);
}
public void setDevicePositionLocked(int position) {
@@ -568,6 +572,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate;
mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling;
mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
+ mBaseDisplayInfo.canHostTasks = mCanHostTasks;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
@@ -927,6 +932,61 @@ final class LogicalDisplay {
return handleLogicalDisplayChangedLocked;
}
+ boolean canHostTasksLocked() {
+ return mCanHostTasks;
+ }
+
+ /**
+ * Sets whether the display can host tasks.
+ *
+ * @param canHostTasks Whether the display can host tasks according to the user's setting.
+ * @return Whether Display Manager should call sendDisplayEventIfEnabledLocked().
+ */
+ boolean setCanHostTasksLocked(boolean canHostTasks) {
+ canHostTasks = validateCanHostTasksLocked(canHostTasks);
+ if (mBaseDisplayInfo.canHostTasks == canHostTasks) {
+ return false;
+ }
+
+ mCanHostTasks = canHostTasks;
+ mBaseDisplayInfo.canHostTasks = canHostTasks;
+ mInfo.set(null);
+ return true;
+ }
+
+ /**
+ * Checks whether the display's ability to host tasks should be determined independently of the
+ * user's setting value. If so, returns the actual validated value based on the display's
+ * usage; otherwise, returns the user's setting value.
+ *
+ * @param canHostTasks Whether the display can host tasks according to the user's setting.
+ * @return Whether the display can actually host task after configuration.
+ */
+ private boolean validateCanHostTasksLocked(boolean canHostTasks) {
+ // The default display can always host tasks.
+ if (getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
+ return true;
+ }
+
+ // The display that should only mirror can never host tasks.
+ if (mPrimaryDisplayDevice.shouldOnlyMirror()) {
+ return false;
+ }
+
+ // The display that has its own content can always host tasks.
+ final boolean isRearDisplay = getDevicePositionLocked() == POSITION_REAR;
+ final boolean ownContent =
+ ((mPrimaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY)
+ != 0)
+ || isRearDisplay;
+ if (ownContent) {
+ return true;
+ }
+
+ return canHostTasks;
+ }
+
/**
* Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
*
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index c0903a9bafac..79592a656409 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -506,9 +506,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return;
}
- Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
- + mDeviceState.getIdentifier() + ", interactive=" + mInteractive
- + ", mBootCompleted=" + mBootCompleted);
// As part of a state transition, we may need to turn off some displays temporarily so that
// the transition is smooth. Plus, on some devices, only one internal displays can be
// on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
@@ -522,6 +519,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
mInteractive, mBootCompleted);
+ Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
+ + mDeviceState.getIdentifier() + ", interactive=" + mInteractive
+ + ", mBootCompleted=" + mBootCompleted + ", wakeDevice=" + wakeDevice
+ + ", sleepDevice=" + sleepDevice);
+
// If all displays are off already, we can just transition here, unless we are trying to
// wake or sleep the device as part of this transition. In that case defer the final
// transition until later once the device is awake/asleep.
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 836f4ede8f57..558afd1da380 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -91,6 +91,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
+ // When a virtual display is created, the mapping (appToken -> ownerUid) is stored here. That
+ // way, when the display is released later, we can retrieve the ownerUid and decrement
+ // the number of virtual displays that exist for that ownerUid. We can't use
+ // Binder.getCallingUid() because the display might be released by the system process and not
+ // the process that created the display.
+ private final ArrayMap<IBinder, Integer> mOwnerUids = new ArrayMap<>();
+
private final int mMaxDevices;
private final int mMaxDevicesPerPackage;
private final SparseIntArray mNoOfDevicesPerPackage = new SparseIntArray();
@@ -194,6 +201,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mVirtualDisplayDevices.put(appToken, device);
if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
mNoOfDevicesPerPackage.put(ownerUid, noOfDevices + 1);
+ mOwnerUids.put(appToken, ownerUid);
}
try {
@@ -205,7 +213,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
Slog.e(TAG, "Virtual Display: error while setting up VirtualDisplayDevice", ex);
- removeVirtualDisplayDeviceLocked(appToken, ownerUid);
+ removeVirtualDisplayDeviceLocked(appToken);
device.destroyLocked(false);
return null;
}
@@ -252,12 +260,10 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
/**
* Release a virtual display that was previously created
* @param appToken The token to identify the display
- * @param ownerUid The UID of the package, used to keep track of and limit the number of
- * displays created per package
* @return The display device that has been removed
*/
- public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken, int ownerUid) {
- VirtualDisplayDevice device = removeVirtualDisplayDeviceLocked(appToken, ownerUid);
+ public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
+ VirtualDisplayDevice device = removeVirtualDisplayDeviceLocked(appToken);
if (device != null) {
Slog.v(TAG, "Release VirtualDisplay " + device.mName);
device.destroyLocked(true);
@@ -299,13 +305,16 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
}
- private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken, int ownerUid) {
- int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
+ private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken) {
if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
- if (noOfDevices <= 1) {
- mNoOfDevicesPerPackage.delete(ownerUid);
- } else {
- mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
+ Integer ownerUid = mOwnerUids.remove(appToken);
+ if (ownerUid != null) {
+ int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
+ if (noOfDevices <= 1) {
+ mNoOfDevicesPerPackage.delete(ownerUid);
+ } else {
+ mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
+ }
}
}
return mVirtualDisplayDevices.remove(appToken);
@@ -378,7 +387,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
@Override
public void binderDied() {
synchronized (getSyncRoot()) {
- removeVirtualDisplayDeviceLocked(mAppToken, mOwnerUid);
+ removeVirtualDisplayDeviceLocked(mAppToken);
Slog.i(TAG, "Virtual display device released because application token died: "
+ mOwnerPackageName);
destroyLocked(false);
@@ -492,6 +501,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mPendingChanges = 0;
}
+ @Override
+ public boolean shouldOnlyMirror() {
+ return mProjection != null || ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0);
+ }
+
public void setSurfaceLocked(Surface surface) {
if (!mStopped && mSurface != surface) {
if (mDisplayState == Display.STATE_ON
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 9e9b899ffa7d..159c30ddf77f 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -78,6 +78,8 @@ public final class BrightnessEvent {
private String mDisplayBrightnessStrategyName;
@AutomaticBrightnessController.AutomaticBrightnessMode
private int mAutoBrightnessMode;
+ private boolean mSlowChange;
+ private float mRampSpeed;
public BrightnessEvent(BrightnessEvent that) {
copyFrom(that);
@@ -126,6 +128,8 @@ public final class BrightnessEvent {
mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled();
mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName();
mAutoBrightnessMode = that.mAutoBrightnessMode;
+ mSlowChange = that.mSlowChange;
+ mRampSpeed = that.mRampSpeed;
}
/**
@@ -163,6 +167,8 @@ public final class BrightnessEvent {
mAutomaticBrightnessEnabled = true;
mDisplayBrightnessStrategyName = "";
mAutoBrightnessMode = AUTO_BRIGHTNESS_MODE_DEFAULT;
+ mSlowChange = false;
+ mRampSpeed = 0;
}
/**
@@ -248,7 +254,9 @@ public final class BrightnessEvent {
+ ", powerFactor=" + mPowerFactor
// Meta
+ ", physDisp=" + mPhysicalDisplayName + "(" + mPhysicalDisplayId + ")"
- + ", logicalId=" + mDisplayId;
+ + ", logicalId=" + mDisplayId
+ + ", slowChange=" + mSlowChange
+ + ", rampSpeed=" + mRampSpeed;
}
@Override
@@ -469,8 +477,8 @@ public final class BrightnessEvent {
return mDisplayBrightnessStrategyName;
}
- public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) {
- this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled;
+ public void setAutomaticBrightnessEnabled(boolean automaticBrightnessEnabled) {
+ mAutomaticBrightnessEnabled = automaticBrightnessEnabled;
}
@AutomaticBrightnessController.AutomaticBrightnessMode
@@ -483,6 +491,14 @@ public final class BrightnessEvent {
mAutoBrightnessMode = mode;
}
+ public void setSlowChange(boolean slowChange) {
+ mSlowChange = slowChange;
+ }
+
+ public void setRampSpeed(float rampSpeed) {
+ mRampSpeed = rampSpeed;
+ }
+
/**
* A utility to stringify flags from a BrightnessEvent
* @return Stringified flags from BrightnessEvent
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 440a271d7f76..860be2028eb3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -32,6 +32,7 @@ import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.util.IndentingPrintWriter;
import android.util.Spline;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
@@ -58,6 +59,7 @@ public class BrightnessClamperController {
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
private final LightSensorController mLightSensorController;
+ private int mDisplayState = Display.STATE_OFF;
private final ClamperChangeListener mClamperChangeListenerExternal;
private final Executor mExecutor;
@@ -149,16 +151,13 @@ public class BrightnessClamperController {
public DisplayBrightnessState clamp(DisplayBrightnessState displayBrightnessState,
DisplayManagerInternal.DisplayPowerRequest request,
float brightnessValue, boolean slowChange, int displayState) {
+ mDisplayState = displayState;
DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(
displayBrightnessState);
builder.setIsSlowChange(slowChange);
builder.setBrightness(brightnessValue);
- if (displayState != STATE_ON) {
- mLightSensorController.stop();
- } else {
- adjustLightSensorSubscription();
- }
+ adjustLightSensorSubscription();
for (int i = 0; i < mModifiers.size(); i++) {
mModifiers.get(i).apply(request, builder);
@@ -230,7 +229,8 @@ public class BrightnessClamperController {
}
private void adjustLightSensorSubscription() {
- if (mModifiers.stream().anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
+ if (mDisplayState == STATE_ON && mModifiers.stream()
+ .anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
mLightSensorController.restart();
} else {
mLightSensorController.stop();
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 7892639fc8ed..52e64905c984 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -168,8 +168,7 @@ public final class ColorDisplayService extends SystemService {
new NightDisplayTintController();
private final TintController mGlobalSaturationTintController =
new GlobalSaturationTintController();
- private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
- new ReduceBrightColorsTintController();
+ private final ReduceBrightColorsTintController mReduceBrightColorsTintController;
@VisibleForTesting
final Handler mHandler;
@@ -201,7 +200,13 @@ public final class ColorDisplayService extends SystemService {
private boolean mEvenDimmerActivated;
public ColorDisplayService(Context context) {
+ this(context, new ReduceBrightColorsTintController());
+ }
+
+ @VisibleForTesting
+ public ColorDisplayService(Context context, ReduceBrightColorsTintController rbcController) {
super(context);
+ mReduceBrightColorsTintController = rbcController;
mHandler = new TintHandler(DisplayThread.get().getLooper());
mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
mUserManager = UserManagerService.getInstance();
@@ -571,27 +576,37 @@ public final class ColorDisplayService extends SystemService {
return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID);
}
- private void onDisplayColorModeChanged(int mode) {
+ @VisibleForTesting
+ void onDisplayColorModeChanged(int mode) {
if (mode == NOT_SET) {
return;
}
+ mReduceBrightColorsTintController.cancelAnimator();
mNightDisplayTintController.cancelAnimator();
mDisplayWhiteBalanceTintController.cancelAnimator();
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+
if (mNightDisplayTintController.isAvailable(getContext())) {
- final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
}
+ if (mReduceBrightColorsTintController.isAvailable(getContext())) {
+ // Different color modes may require different coefficients to be loaded for RBC.
+ // Re-set up RBC so that it can recalculate its transform matrix with new values.
+ mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
+ onReduceBrightColorsStrengthLevelChanged(); // Trigger matrix recalc + updates
+ }
+
// dtm.setColorMode() needs to be called before
// updateDisplayWhiteBalanceStatus(), this is because the latter calls
// DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent
// on the state of DisplayTransformManager.
- final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(),
+ mReduceBrightColorsTintController.getMatrix(),
getCompositionColorSpace(mode));
if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index a76c427bec0e..cb7b1773e47e 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -265,7 +265,7 @@ public class DisplayTransformManager {
/**
* Sets color mode and updates night display transform values.
*/
- public boolean setColorMode(int colorMode, float[] nightDisplayMatrix,
+ public boolean setColorMode(int colorMode, float[] nightDisplayMatrix, float[] rbcMatrix,
int compositionColorMode) {
if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
applySaturation(COLOR_SATURATION_NATURAL);
@@ -285,7 +285,11 @@ public class DisplayTransformManager {
setDisplayColor(colorMode, compositionColorMode);
}
+ // These are close to the setDisplayColor() call to reduce delay between
+ // setting these matrixes and updating the color mode. Without this proximity
+ // of calls, updates to color mode can result in flicker.
setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
+ setColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS, rbcMatrix);
updateConfiguration();
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 45106f54cb9f..85b6bbb40b91 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -46,10 +46,6 @@ public class DisplayManagerFlags {
Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
Flags::enableConnectedDisplayManagement);
- private final FlagState mNbmControllerFlagState = new FlagState(
- Flags.FLAG_ENABLE_NBM_CONTROLLER,
- Flags::enableNbmController);
-
private final FlagState mHdrClamperFlagState = new FlagState(
Flags.FLAG_ENABLE_HDR_CLAMPER,
Flags::enableHdrClamper);
@@ -93,6 +89,10 @@ public class DisplayManagerFlags {
com.android.graphics.surfaceflinger.flags.Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
com.android.graphics.surfaceflinger.flags.Flags::enableSmallAreaDetection);
+ private final FlagState mDisplayConfigErrorHalFlagState = new FlagState(
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_DISPLAY_CONFIG_ERROR_HAL,
+ com.android.graphics.surfaceflinger.flags.Flags::displayConfigErrorHal);
+
private final FlagState mBrightnessIntRangeUserPerceptionFlagState = new FlagState(
Flags.FLAG_BRIGHTNESS_INT_RANGE_USER_PERCEPTION,
Flags::brightnessIntRangeUserPerception);
@@ -256,6 +256,10 @@ public class DisplayManagerFlags {
Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS,
Flags::displayListenerPerformanceImprovements
);
+ private final FlagState mEnableDisplayContentModeManagementFlagState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
+ Flags::enableDisplayContentModeManagement
+ );
private final FlagState mSubscribeGranularDisplayEvents = new FlagState(
Flags.FLAG_SUBSCRIBE_GRANULAR_DISPLAY_EVENTS,
@@ -274,11 +278,6 @@ public class DisplayManagerFlags {
return mConnectedDisplayManagementFlagState.isEnabled();
}
- /** Returns whether NBM Controller is enabled or not. */
- public boolean isNbmControllerEnabled() {
- return mNbmControllerFlagState.isEnabled();
- }
-
/** Returns whether hdr clamper is enabled on not. */
public boolean isHdrClamperEnabled() {
return mHdrClamperFlagState.isEnabled();
@@ -357,6 +356,10 @@ public class DisplayManagerFlags {
return mSmallAreaDetectionFlagState.isEnabled();
}
+ public boolean isDisplayConfigErrorHalEnabled() {
+ return mDisplayConfigErrorHalFlagState.isEnabled();
+ }
+
public boolean isBrightnessIntRangeUserPerceptionEnabled() {
return mBrightnessIntRangeUserPerceptionFlagState.isEnabled();
}
@@ -556,6 +559,10 @@ public class DisplayManagerFlags {
return mDisplayListenerPerformanceImprovementsFlagState.isEnabled();
}
+ public boolean isDisplayContentModeManagementEnabled() {
+ return mEnableDisplayContentModeManagementFlagState.isEnabled();
+ }
+
/**
* @return {@code true} if the flag for subscribing to granular display events is enabled
*/
@@ -579,10 +586,10 @@ public class DisplayManagerFlags {
pw.println(" " + mExternalDisplayLimitModeState);
pw.println(" " + mDisplayTopology);
pw.println(" " + mHdrClamperFlagState);
- pw.println(" " + mNbmControllerFlagState);
pw.println(" " + mPowerThrottlingClamperFlagState);
pw.println(" " + mEvenDimmerFlagState);
pw.println(" " + mSmallAreaDetectionFlagState);
+ pw.println(" " + mDisplayConfigErrorHalFlagState);
pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
pw.println(" " + mRestrictDisplayModes);
pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
@@ -618,6 +625,7 @@ public class DisplayManagerFlags {
pw.println(" " + mEnablePluginManagerFlagState);
pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
pw.println(" " + mSubscribeGranularDisplayEvents);
+ pw.println(" " + mEnableDisplayContentModeManagementFlagState);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 123b7dfbf843..3358f723709c 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -37,14 +37,6 @@ flag {
}
flag {
- name: "enable_nbm_controller"
- namespace: "display_manager"
- description: "Feature flag for Normal Brightness Mode Controller"
- bug: "299527549"
- is_fixed_read_only: true
-}
-
-flag {
name: "enable_hdr_clamper"
namespace: "display_manager"
description: "Feature flag for HDR Clamper"
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 8423e1911764..02e2882442bf 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -136,6 +136,7 @@ public class DisplayModeDirector {
private final ProximitySensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+ private final ModeChangeObserver mModeChangeObserver;
@Nullable
private final SystemRequestObserver mSystemRequestObserver;
@@ -247,6 +248,7 @@ public class DisplayModeDirector {
mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage, injector);
mSensorObserver = new ProximitySensorObserver(mVotesStorage, injector);
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
+ mModeChangeObserver = new ModeChangeObserver(mVotesStorage, injector, handler.getLooper());
mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
if (displayManagerFlags.isRestrictDisplayModesEnabled()) {
@@ -275,6 +277,9 @@ public class DisplayModeDirector {
mSensorObserver.observe();
mHbmObserver.observe();
mSkinThermalStatusObserver.observe();
+ if (mDisplayManagerFlags.isDisplayConfigErrorHalEnabled()) {
+ mModeChangeObserver.observe();
+ }
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
new file mode 100644
index 000000000000..bbc13cc6ae7e
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.os.Looper;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayEventReceiver;
+
+import com.android.internal.annotations.KeepForWeakReference;
+
+import java.util.HashSet;
+import java.util.Set;
+
+final class ModeChangeObserver {
+ private static final String TAG = "ModeChangeObserver";
+
+ private final VotesStorage mVotesStorage;
+ private final DisplayModeDirector.Injector mInjector;
+
+ @SuppressWarnings("unused")
+ @KeepForWeakReference
+ private DisplayEventReceiver mModeChangeListener;
+ private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
+ private Looper mLooper;
+
+ ModeChangeObserver(VotesStorage votesStorage, DisplayModeDirector.Injector injector,
+ Looper looper) {
+ mVotesStorage = votesStorage;
+ mInjector = injector;
+ mLooper = looper;
+ }
+
+ void observe() {
+ mModeChangeListener = new DisplayEventReceiver(mLooper) {
+ @Override
+ public void onModeRejected(long physicalDisplayId, int modeId) {
+ Slog.d(TAG, "Mode Rejected event received");
+ int displayId = getLogicalDisplayId(physicalDisplayId);
+ if (displayId < 0) {
+ Slog.e(TAG, "Logical Display Id not found");
+ return;
+ }
+ populateRejectedModesListByDisplay(displayId, modeId);
+ }
+
+ @Override
+ public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
+ Slog.d(TAG, "Hotplug event received");
+ if (!connected) {
+ int displayId = getLogicalDisplayId(physicalDisplayId);
+ if (displayId < 0) {
+ Slog.e(TAG, "Logical Display Id not found");
+ return;
+ }
+ clearRejectedModesListByDisplay(displayId);
+ }
+ }
+ };
+ }
+
+ private int getLogicalDisplayId(long rejectedModePhysicalDisplayId) {
+ Display[] displays = mInjector.getDisplays();
+
+ for (Display display : displays) {
+ DisplayAddress address = display.getAddress();
+ if (address instanceof DisplayAddress.Physical physical) {
+ long physicalDisplayId = physical.getPhysicalDisplayId();
+ if (physicalDisplayId == rejectedModePhysicalDisplayId) {
+ return display.getDisplayId();
+ }
+ }
+ }
+ return -1;
+ }
+
+ private void populateRejectedModesListByDisplay(int displayId, int rejectedModeId) {
+ Set<Integer> alreadyRejectedModes = mRejectedModesByDisplay.get(displayId);
+ if (alreadyRejectedModes == null) {
+ alreadyRejectedModes = new HashSet<>();
+ mRejectedModesByDisplay.put(displayId, alreadyRejectedModes);
+ }
+ alreadyRejectedModes.add(rejectedModeId);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES,
+ Vote.forRejectedModes(alreadyRejectedModes));
+ }
+
+ private void clearRejectedModesListByDisplay(int displayId) {
+ mRejectedModesByDisplay.remove(displayId);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES, null);
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/RejectedModesVote.java b/services/core/java/com/android/server/display/mode/RejectedModesVote.java
new file mode 100644
index 000000000000..db8c8527844b
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RejectedModesVote.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class RejectedModesVote implements Vote {
+
+ final Set<Integer> mModeIds;
+
+ RejectedModesVote(Set<Integer> modeIds) {
+ mModeIds = Collections.unmodifiableSet(modeIds);
+ }
+ @Override
+ public void updateSummary(@NonNull VoteSummary summary) {
+ summary.rejectedModeIds.addAll(mModeIds);
+ }
+
+ @Override
+ public String toString() {
+ return "RejectedModesVote{ mModeIds=" + mModeIds + " }";
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index f5abb0561ce7..428ccedf8760 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -25,6 +25,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
interface Vote {
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
@@ -82,68 +83,73 @@ interface Vote {
int PRIORITY_APP_REQUEST_SIZE = 7;
+ // PRIORITY_REJECTED_MODES rejects the modes for which the mode config failed
+ // so that the modeset can be retried for next available mode after filtering
+ // out the rejected modes for the connected display
+ int PRIORITY_REJECTED_MODES = 8;
+
// PRIORITY_USER_SETTING_PEAK_REFRESH_RATE restricts physical refresh rate to
// [0, max(PEAK, MIN)], depending on user settings peakRR/minRR values
- int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 8;
+ int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 9;
// PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE has a higher priority than
// PRIORITY_USER_SETTING_PEAK_REFRESH_RATE and will limit render rate to [0, max(PEAK, MIN)]
// in case physical refresh rate vote is discarded (due to other high priority votes),
// render rate vote can still apply
- int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 9;
+ int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 10;
// Restrict all displays physical refresh rate to 60Hz when external display is connected.
// It votes [59Hz, 61Hz].
- int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 10;
+ int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 11;
// PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE has a higher priority than
// PRIORITY_SYNCHRONIZED_REFRESH_RATE and will limit render rate to [59Hz, 61Hz].
// In case physical refresh rate vote discarded (due to physical refresh rate not supported),
// render rate vote can still apply.
- int PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE = 11;
+ int PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE = 12;
// Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
- int PRIORITY_LIMIT_MODE = 12;
+ int PRIORITY_LIMIT_MODE = 13;
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
- int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 13;
+ int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 14;
// For concurrent displays we want to limit refresh rate on all displays
- int PRIORITY_LAYOUT_LIMITED_REFRESH_RATE = 14;
+ int PRIORITY_LAYOUT_LIMITED_REFRESH_RATE = 15;
// For concurrent displays we want to limit refresh rate on all displays
- int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 15;
+ int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 16;
// For internal application to limit display modes to specific ids
- int PRIORITY_SYSTEM_REQUESTED_MODES = 16;
+ int PRIORITY_SYSTEM_REQUESTED_MODES = 17;
// PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
// Settings.Global.LOW_POWER_MODE is on.
// Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
// higher priority votes), render rate limit can still apply
- int PRIORITY_LOW_POWER_MODE_MODES = 17;
+ int PRIORITY_LOW_POWER_MODE_MODES = 18;
// PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 18;
+ int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 19;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 19;
+ int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 20;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- int PRIORITY_SKIN_TEMPERATURE = 20;
+ int PRIORITY_SKIN_TEMPERATURE = 21;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- int PRIORITY_PROXIMITY = 21;
+ int PRIORITY_PROXIMITY = 22;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- int PRIORITY_UDFPS = 22;
+ int PRIORITY_UDFPS = 23;
@IntDef(prefix = { "PRIORITY_" }, value = {
PRIORITY_DEFAULT_RENDER_FRAME_RATE,
@@ -154,6 +160,7 @@ interface Vote {
PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
PRIORITY_APP_REQUEST_SIZE,
+ PRIORITY_REJECTED_MODES,
PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
PRIORITY_SYNCHRONIZED_REFRESH_RATE,
@@ -245,6 +252,10 @@ interface Vote {
return new SupportedModesVote(modeIds);
}
+ static Vote forRejectedModes(Set<Integer> modeIds) {
+ return new RejectedModesVote(modeIds);
+ }
+
static String priorityToString(int priority) {
switch (priority) {
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
@@ -253,6 +264,8 @@ interface Vote {
return "PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE";
case PRIORITY_APP_REQUEST_SIZE:
return "PRIORITY_APP_REQUEST_SIZE";
+ case PRIORITY_REJECTED_MODES:
+ return "PRIORITY_REJECTED_MODES";
case PRIORITY_DEFAULT_RENDER_FRAME_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE:
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index 00a922630d8e..41664930fc2e 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -55,6 +55,11 @@ final class VoteSummary {
@Nullable
public List<Integer> supportedModeIds;
+ /**
+ * set of rejected modes due to mode config failure for connected display
+ */
+ public Set<Integer> rejectedModeIds = new HashSet<>();
+
final boolean mIsDisplayResolutionRangeVotingEnabled;
private final boolean mSupportedModesVoteEnabled;
@@ -132,6 +137,9 @@ final class VoteSummary {
if (!validateModeSupported(mode)) {
continue;
}
+ if (!validateModeRejected(mode)) {
+ continue;
+ }
if (!validateModeSize(mode)) {
continue;
}
@@ -285,6 +293,22 @@ final class VoteSummary {
return false;
}
+ private boolean validateModeRejected(Display.Mode mode) {
+ if (rejectedModeIds == null) {
+ return true;
+ }
+ if (!rejectedModeIds.contains(mode.getModeId())) {
+ return true;
+ }
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Discarding mode" + mode.getModeId()
+ + ", is a rejectedMode"
+ + ": mode.modeId=" + mode.getModeId()
+ + ", rejectedModeIds=" + rejectedModeIds);
+ }
+ return false;
+ }
+
private boolean validateRefreshRatesSupported(Display.Mode mode) {
if (supportedRefreshRates == null || !mSupportedModesVoteEnabled) {
return true;
@@ -397,6 +421,7 @@ final class VoteSummary {
requestedRefreshRates.clear();
supportedRefreshRates = null;
supportedModeIds = null;
+ rejectedModeIds.clear();
if (mLoggingEnabled) {
Slog.i(TAG, "Summary reset: " + this);
}
@@ -421,6 +446,7 @@ final class VoteSummary {
+ ", requestRefreshRates=" + requestedRefreshRates
+ ", supportedRefreshRates=" + supportedRefreshRates
+ ", supportedModeIds=" + supportedModeIds
+ + ", rejectedModeIds=" + rejectedModeIds
+ ", mIsDisplayResolutionRangeVotingEnabled="
+ mIsDisplayResolutionRangeVotingEnabled
+ ", mSupportedModesVoteEnabled=" + mSupportedModesVoteEnabled
diff --git a/services/core/java/com/android/server/display/plugin/PluginEventStorage.java b/services/core/java/com/android/server/display/plugin/PluginEventStorage.java
new file mode 100644
index 000000000000..c58ba556bcb6
--- /dev/null
+++ b/services/core/java/com/android/server/display/plugin/PluginEventStorage.java
@@ -0,0 +1,134 @@
+/*
+ * 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.server.display.plugin;
+
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.util.RingBuffer;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+class PluginEventStorage {
+ private static final long TIME_FRAME_LENGTH = 60_000;
+ private static final long MIN_EVENT_DELAY = 500;
+ private static final int MAX_TIME_FRAMES = 10;
+ // not thread safe
+ private static final SimpleDateFormat sDateFormat = new SimpleDateFormat(
+ "MM-dd HH:mm:ss.SSS", Locale.US);
+
+ RingBuffer<TimeFrame> mEvents = new RingBuffer<>(
+ TimeFrame::new, TimeFrame[]::new, MAX_TIME_FRAMES);
+
+ private final Map<PluginType<?>, Long> mEventTimes = new HashMap<>();
+ private long mTimeFrameStart = 0;
+ private final Map<PluginType<?>, EventCounter> mCounters = new HashMap<>();
+
+ <T> void onValueUpdated(PluginType<T> type) {
+ long eventTime = System.currentTimeMillis();
+ if (eventTime - TIME_FRAME_LENGTH > mTimeFrameStart) { // event is in next TimeFrame
+ closeCurrentTimeFrame();
+ mTimeFrameStart = eventTime;
+ }
+ updateCurrentTimeFrame(type, eventTime);
+ }
+
+ private void closeCurrentTimeFrame() {
+ if (!mCounters.isEmpty()) {
+ mEvents.append(new TimeFrame(
+ mTimeFrameStart, mTimeFrameStart + TIME_FRAME_LENGTH, mCounters));
+ mCounters.clear();
+ }
+ }
+
+ private <T> void updateCurrentTimeFrame(PluginType<T> type, long eventTime) {
+ EventCounter counter = mCounters.get(type);
+ long previousTimestamp = mEventTimes.getOrDefault(type, 0L);
+ if (counter == null) {
+ counter = new EventCounter();
+ mCounters.put(type, counter);
+ }
+ counter.increase(eventTime, previousTimestamp);
+ mEventTimes.put(type, eventTime);
+ }
+
+ List<TimeFrame> getTimeFrames() {
+ List<TimeFrame> timeFrames = new ArrayList<>(Arrays.stream(mEvents.toArray()).toList());
+ timeFrames.add(new TimeFrame(
+ mTimeFrameStart, System.currentTimeMillis(), mCounters));
+ return timeFrames;
+ }
+
+ static class TimeFrame {
+ private final long mStart;
+ private final long mEnd;
+ private final Map<PluginType<?>, EventCounter> mCounters;
+
+ private TimeFrame() {
+ this(0, 0, Map.of());
+ }
+
+ private TimeFrame(long start, long end, Map<PluginType<?>, EventCounter> counters) {
+ mStart = start;
+ mEnd = end;
+ mCounters = new HashMap<>(counters);
+ }
+
+ @SuppressWarnings("JavaUtilDate")
+ void dump(PrintWriter pw) {
+ pw.append("TimeFrame:[")
+ .append(sDateFormat.format(new Date(mStart)))
+ .append(" - ")
+ .append(sDateFormat.format(new Date(mEnd)))
+ .println("]:");
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ if (mCounters.isEmpty()) {
+ ipw.println("NO EVENTS");
+ } else {
+ for (Map.Entry<PluginType<?>, EventCounter> entry: mCounters.entrySet()) {
+ ipw.append(entry.getKey().mName).append(" -> {");
+ entry.getValue().dump(ipw);
+ ipw.println("}");
+ }
+ }
+ }
+ }
+
+ private static class EventCounter {
+ private int mEventCounter = 0;
+ private int mFastEventCounter = 0;
+
+ private void increase(long timestamp, long previousTimestamp) {
+ mEventCounter++;
+ if (timestamp - previousTimestamp < MIN_EVENT_DELAY) {
+ mFastEventCounter++;
+ }
+ }
+
+ private void dump(PrintWriter pw) {
+ pw.append("Count:").append(String.valueOf(mEventCounter))
+ .append("; Fast:").append(String.valueOf(mFastEventCounter));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index 2bcea777e681..dd3415fb614d 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -25,6 +25,7 @@ import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -39,6 +40,8 @@ public class PluginStorage {
private final Map<PluginType<?>, Object> mValues = new HashMap<>();
@GuardedBy("mLock")
private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
+ @GuardedBy("mLock")
+ private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
/**
* Updates value in storage and forwards it to corresponding listeners.
@@ -50,6 +53,7 @@ public class PluginStorage {
Set<PluginManager.PluginChangeListener<T>> localListeners;
synchronized (mLock) {
mValues.put(type, value);
+ mPluginEventStorage.onValueUpdated(type);
ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
localListeners = new LinkedHashSet<>(container.mListeners);
}
@@ -91,13 +95,19 @@ public class PluginStorage {
Map<PluginType<?>, Object> localValues;
@SuppressWarnings("rawtypes")
Map<PluginType, Set> localListeners = new HashMap<>();
+ List<PluginEventStorage.TimeFrame> timeFrames;
synchronized (mLock) {
+ timeFrames = mPluginEventStorage.getTimeFrames();
localValues = new HashMap<>(mValues);
mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
}
pw.println("PluginStorage:");
pw.println("values=" + localValues);
pw.println("listeners=" + localListeners);
+ pw.println("PluginEventStorage:");
+ for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
+ timeFrame.dump(pw);
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 236333ee433d..18ae0446b3e0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -303,6 +303,10 @@ abstract class HdmiCecFeatureAction {
return mSource.getDeviceInfo().getPhysicalAddress();
}
+ protected final int getServicePath() {
+ return mService.getPhysicalAddress();
+ }
+
protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7505c710f483..424102cbdd89 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -212,11 +212,11 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiConfig.TIMEOUT_MS);
}
- launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
- reason != HdmiControlService.INITIATED_BY_BOOT_UP);
resetSelectRequestBuffer();
launchDeviceDiscovery();
startQueuedActions();
+ final boolean routingForBootup = reason != HdmiControlService.INITIATED_BY_ENABLE_CEC
+ && reason != HdmiControlService.INITIATED_BY_BOOT_UP;
List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
.getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
if (bufferedActiveSource.isEmpty()) {
@@ -227,14 +227,8 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
- if (!mService.getLocalActiveSource().isValid()
- && result != HdmiControlManager.RESULT_SUCCESS) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
- getDeviceInfo().getLogicalAddress(),
- getDeviceInfo().getPhysicalAddress()));
- updateActiveSource(getDeviceInfo().getLogicalAddress(),
- getDeviceInfo().getPhysicalAddress(),
- "RequestActiveSourceAction#finishWithCallback()");
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ launchRoutingControl(routingForBootup);
}
}
}));
@@ -1384,8 +1378,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
} else {
int activePath = mService.getPhysicalAddress();
setActivePath(activePath);
- if (!routingForBootup
- && !mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+ if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildActiveSource(
getDeviceInfo().getLogicalAddress(), activePath));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 256905d50dc1..9f6322d9b229 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -152,7 +152,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
// If the device wasn´t the active source yet,
// this makes it the active source and wakes it up.
mSource.mService.setAndBroadcastActiveSourceFromOneDeviceType(
- mTargetAddress, getSourcePath(), "OneTouchPlayAction#broadcastActiveSource()");
+ mTargetAddress, getServicePath(),
+ "OneTouchPlayAction#broadcastActiveSource()");
// When OneTouchPlay is called, client side should be responsible to send out the intent
// of which internal source, for example YouTube, it would like to switch to.
// Here we only update the active port and the active source records in the local
diff --git a/services/core/java/com/android/server/input/InputDataStore.java b/services/core/java/com/android/server/input/InputDataStore.java
new file mode 100644
index 000000000000..e8f21fe8fb74
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputDataStore.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 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.server.input;
+
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.InputGestureData;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages persistent state recorded by the input manager service as a set of XML files.
+ * Caller must acquire lock on the data store before accessing it.
+ */
+public final class InputDataStore {
+ private static final String TAG = "InputDataStore";
+
+ private static final String INPUT_MANAGER_DIRECTORY = "input";
+
+ private static final String TAG_ROOT = "root";
+
+ private static final String TAG_INPUT_GESTURE_LIST = "input_gesture_list";
+ private static final String TAG_INPUT_GESTURE = "input_gesture";
+ private static final String TAG_KEY_TRIGGER = "key_trigger";
+ private static final String TAG_TOUCHPAD_TRIGGER = "touchpad_trigger";
+ private static final String TAG_APP_LAUNCH_DATA = "app_launch_data";
+
+ private static final String ATTR_KEY_TRIGGER_KEYCODE = "keycode";
+ private static final String ATTR_KEY_TRIGGER_MODIFIER_STATE = "modifiers";
+ private static final String ATTR_KEY_GESTURE_TYPE = "key_gesture_type";
+ private static final String ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE = "touchpad_gesture_type";
+ private static final String ATTR_APP_LAUNCH_DATA_CATEGORY = "category";
+ private static final String ATTR_APP_LAUNCH_DATA_ROLE = "role";
+ private static final String ATTR_APP_LAUNCH_DATA_PACKAGE_NAME = "package_name";
+ private static final String ATTR_APP_LAUNCH_DATA_CLASS_NAME = "class_name";
+
+ private final FileInjector mInputGestureFileInjector;
+
+ public InputDataStore() {
+ this(new FileInjector("input_gestures.xml"));
+ }
+
+ public InputDataStore(final FileInjector inputGestureFileInjector) {
+ mInputGestureFileInjector = inputGestureFileInjector;
+ }
+
+ /**
+ * Reads from the local disk storage the list of customized input gestures.
+ *
+ * @param userId The user id to fetch the gestures for.
+ * @return List of {@link InputGestureData} which the user previously customized.
+ */
+ public List<InputGestureData> loadInputGestures(int userId) {
+ List<InputGestureData> inputGestureDataList;
+ try {
+ final InputStream inputStream = mInputGestureFileInjector.openRead(userId);
+ inputGestureDataList = readInputGesturesXml(inputStream, false);
+ inputStream.close();
+ } catch (IOException exception) {
+ // In case we are unable to read from the file on disk or another IO operation error,
+ // fail gracefully.
+ Slog.e(TAG, "Failed to read from " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), exception);
+ return List.of();
+ } catch (Exception exception) {
+ // In the case of any other exception, we want it to bubble up as this would be due
+ // to malformed trusted XML data.
+ throw new RuntimeException(
+ "Failed to read from " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), exception);
+ }
+ return inputGestureDataList;
+ }
+
+ /**
+ * Writes to the local disk storage the list of customized input gestures provided as a param.
+ *
+ * @param userId The user id to store the {@link InputGestureData} list under.
+ * @param inputGestureDataList The list of custom input gestures for the given {@code userId}.
+ */
+ public void saveInputGestures(int userId, List<InputGestureData> inputGestureDataList) {
+ FileOutputStream outputStream = null;
+ try {
+ outputStream = mInputGestureFileInjector.startWrite(userId);
+ writeInputGestureXml(outputStream, false, inputGestureDataList);
+ mInputGestureFileInjector.finishWrite(userId, outputStream, true);
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to write to file " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), e);
+ mInputGestureFileInjector.finishWrite(userId, outputStream, false);
+ }
+ }
+
+ @VisibleForTesting
+ List<InputGestureData> readInputGesturesXml(InputStream stream, boolean utf8Encoded)
+ throws XmlPullParserException, IOException {
+ List<InputGestureData> inputGestureDataList = new ArrayList<>();
+ TypedXmlPullParser parser;
+ if (utf8Encoded) {
+ parser = Xml.newFastPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ } else {
+ parser = Xml.resolvePullParser(stream);
+ }
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final String tag = parser.getName();
+ if (TAG_ROOT.equals(tag)) {
+ continue;
+ }
+
+ if (TAG_INPUT_GESTURE_LIST.equals(tag)) {
+ inputGestureDataList.addAll(readInputGestureListFromXml(parser));
+ }
+ }
+ return inputGestureDataList;
+ }
+
+ private InputGestureData readInputGestureFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException, IllegalArgumentException {
+ InputGestureData.Builder builder = new InputGestureData.Builder();
+ builder.setKeyGestureType(parser.getAttributeInt(null, ATTR_KEY_GESTURE_TYPE));
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ // If the parser has left the initial scope when it was called, break out.
+ if (outerDepth > parser.getDepth()) {
+ throw new RuntimeException(
+ "Parser has left the initial scope of the tag that was being parsed on "
+ + "line number: "
+ + parser.getLineNumber());
+ }
+
+ // If the parser has reached the closing tag for the Input Gesture, break out.
+ if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_INPUT_GESTURE)) {
+ break;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tag = parser.getName();
+ if (TAG_KEY_TRIGGER.equals(tag)) {
+ builder.setTrigger(InputGestureData.createKeyTrigger(
+ parser.getAttributeInt(null, ATTR_KEY_TRIGGER_KEYCODE),
+ parser.getAttributeInt(null, ATTR_KEY_TRIGGER_MODIFIER_STATE)));
+ } else if (TAG_TOUCHPAD_TRIGGER.equals(tag)) {
+ builder.setTrigger(InputGestureData.createTouchpadTrigger(
+ parser.getAttributeInt(null, ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE)));
+ } else if (TAG_APP_LAUNCH_DATA.equals(tag)) {
+ final String roleValue = parser.getAttributeValue(null, ATTR_APP_LAUNCH_DATA_ROLE);
+ final String categoryValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_CATEGORY);
+ final String classNameValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_CLASS_NAME);
+ final String packageNameValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_PACKAGE_NAME);
+ final AppLaunchData appLaunchData = AppLaunchData.createLaunchData(categoryValue,
+ roleValue, packageNameValue, classNameValue);
+ if (appLaunchData != null) {
+ builder.setAppLaunchData(appLaunchData);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ private List<InputGestureData> readInputGestureListFromXml(TypedXmlPullParser parser) throws
+ XmlPullParserException, IOException {
+ List<InputGestureData> inputGestureDataList = new ArrayList<>();
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ // If the parser has left the initial scope when it was called, break out.
+ if (outerDepth > parser.getDepth()) {
+ throw new RuntimeException(
+ "Parser has left the initial scope of the tag that was being parsed on "
+ + "line number: "
+ + parser.getLineNumber());
+ }
+
+ // If the parser has reached the closing tag for the Input Gesture List, break out.
+ if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_INPUT_GESTURE_LIST)) {
+ break;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tag = parser.getName();
+ if (TAG_INPUT_GESTURE.equals(tag)) {
+ try {
+ inputGestureDataList.add(readInputGestureFromXml(parser));
+ } catch (IllegalArgumentException exception) {
+ Slog.w(TAG, "Invalid parameters for input gesture data: ", exception);
+ continue;
+ }
+ }
+ }
+ return inputGestureDataList;
+ }
+
+ @VisibleForTesting
+ void writeInputGestureXml(OutputStream stream, boolean utf8Encoded,
+ List<InputGestureData> inputGestureDataList) throws IOException {
+ final TypedXmlSerializer serializer;
+ if (utf8Encoded) {
+ serializer = Xml.newFastSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ } else {
+ serializer = Xml.resolveSerializer(stream);
+ }
+
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_ROOT);
+ writeInputGestureListToXml(serializer, inputGestureDataList);
+ serializer.endTag(null, TAG_ROOT);
+ serializer.endDocument();
+ }
+
+ private void writeInputGestureToXml(TypedXmlSerializer serializer,
+ InputGestureData inputGestureData) throws IOException {
+ serializer.startTag(null, TAG_INPUT_GESTURE);
+ serializer.attributeInt(null, ATTR_KEY_GESTURE_TYPE,
+ inputGestureData.getAction().keyGestureType());
+
+ final InputGestureData.Trigger trigger = inputGestureData.getTrigger();
+ if (trigger instanceof InputGestureData.KeyTrigger keyTrigger) {
+ serializer.startTag(null, TAG_KEY_TRIGGER);
+ serializer.attributeInt(null, ATTR_KEY_TRIGGER_KEYCODE, keyTrigger.getKeycode());
+ serializer.attributeInt(null, ATTR_KEY_TRIGGER_MODIFIER_STATE,
+ keyTrigger.getModifierState());
+ serializer.endTag(null, TAG_KEY_TRIGGER);
+ } else if (trigger instanceof InputGestureData.TouchpadTrigger touchpadTrigger) {
+ serializer.startTag(null, TAG_TOUCHPAD_TRIGGER);
+ serializer.attributeInt(null, ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE,
+ touchpadTrigger.getTouchpadGestureType());
+ serializer.endTag(null, TAG_TOUCHPAD_TRIGGER);
+ }
+
+ if (inputGestureData.getAction().appLaunchData() != null) {
+ serializer.startTag(null, TAG_APP_LAUNCH_DATA);
+ final AppLaunchData appLaunchData = inputGestureData.getAction().appLaunchData();
+ if (appLaunchData instanceof AppLaunchData.RoleData roleData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_ROLE, roleData.getRole());
+ } else if (appLaunchData
+ instanceof AppLaunchData.CategoryData categoryData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_CATEGORY,
+ categoryData.getCategory());
+ } else if (appLaunchData instanceof AppLaunchData.ComponentData componentData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_PACKAGE_NAME,
+ componentData.getPackageName());
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_CLASS_NAME,
+ componentData.getClassName());
+ }
+ serializer.endTag(null, TAG_APP_LAUNCH_DATA);
+ }
+
+ serializer.endTag(null, TAG_INPUT_GESTURE);
+ }
+
+ private void writeInputGestureListToXml(TypedXmlSerializer serializer,
+ List<InputGestureData> inputGestureDataList) throws IOException {
+ serializer.startTag(null, TAG_INPUT_GESTURE_LIST);
+ for (final InputGestureData inputGestureData : inputGestureDataList) {
+ writeInputGestureToXml(serializer, inputGestureData);
+ }
+ serializer.endTag(null, TAG_INPUT_GESTURE_LIST);
+ }
+
+ @VisibleForTesting
+ static class FileInjector {
+ private final SparseArray<AtomicFile> mAtomicFileMap = new SparseArray<>();
+ private final String mFileName;
+
+ FileInjector(String fileName) {
+ mFileName = fileName;
+ }
+
+ InputStream openRead(int userId) throws FileNotFoundException {
+ return getAtomicFileForUserId(userId).openRead();
+ }
+
+ FileOutputStream startWrite(int userId) throws IOException {
+ return getAtomicFileForUserId(userId).startWrite();
+ }
+
+ void finishWrite(int userId, FileOutputStream os, boolean success) {
+ if (success) {
+ getAtomicFileForUserId(userId).finishWrite(os);
+ } else {
+ getAtomicFileForUserId(userId).failWrite(os);
+ }
+ }
+
+ AtomicFile getAtomicFileForUserId(int userId) {
+ if (!mAtomicFileMap.contains(userId)) {
+ mAtomicFileMap.put(userId, new AtomicFile(new File(
+ Environment.buildPath(Environment.getDataSystemDeDirectory(userId),
+ INPUT_MANAGER_DIRECTORY), mFileName)));
+ }
+ return mAtomicFileMap.get(userId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 265e4531a10a..bc44fed21f2d 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.graphics.PointF;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.input.KeyGestureEvent;
import android.os.IBinder;
@@ -47,6 +48,12 @@ public abstract class InputManagerInternal {
public abstract void setDisplayViewports(List<DisplayViewport> viewports);
/**
+ * Called by {@link com.android.server.display.DisplayManagerService} to inform InputManager
+ * about changes in the displays topology.
+ */
+ public abstract void setDisplayTopology(DisplayTopology topology);
+
+ /**
* Called by the power manager to tell the input manager whether it should start
* watching for wake events on given displays.
*
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9eba926ab482..559b4ae64e50 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -34,6 +34,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionManuallyEnforced;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.bluetooth.BluetoothAdapter;
@@ -51,6 +52,7 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.input.AidlInputGestureData;
import android.hardware.input.HostUsiVersion;
@@ -352,6 +354,9 @@ public class InputManagerService extends IInputManager.Stub
// Manages loading PointerIcons
private final PointerIconCache mPointerIconCache;
+ // Manages storage and retrieval of input data.
+ private final InputDataStore mInputDataStore;
+
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -501,7 +506,9 @@ public class InputManagerService extends IInputManager.Stub
injector.getUEventManager());
mKeyboardBacklightController = injector.getKeyboardBacklightController(mNative);
mStickyModifierStateController = new StickyModifierStateController();
- mKeyGestureController = new KeyGestureController(mContext, injector.getLooper());
+ mInputDataStore = new InputDataStore();
+ mKeyGestureController = new KeyGestureController(mContext, injector.getLooper(),
+ mInputDataStore);
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
mNative);
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -655,6 +662,10 @@ public class InputManagerService extends IInputManager.Stub
mNative.setPointerDisplayId(mWindowManagerCallbacks.getPointerDisplayId());
}
+ private void setDisplayTopologyInternal(DisplayTopology topology) {
+ mNative.setDisplayTopology(topology.getGraph());
+ }
+
/**
* Gets the current state of a key or button by key code.
* @param deviceId The input device id, or -1 to consult all devices.
@@ -2615,38 +2626,14 @@ public class InputManagerService extends IInputManager.Stub
return mWindowManagerCallbacks.interceptUnhandledKey(event, focus);
}
+ @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) {
- int deviceId = event.getDeviceId();
- boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
- && !event.isCancelled();
- switch (event.getKeyGestureType()) {
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
- if (complete) {
- mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
- if (complete) {
- mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
- // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
- if (complete) {
- mNative.toggleCapsLock(deviceId);
- }
- return true;
- default:
- return false;
-
- }
+ return InputManagerService.this.handleKeyGestureEvent(event);
}
@Override
@@ -2656,6 +2643,10 @@ public class InputManagerService extends IInputManager.Stub
case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
return true;
default:
return false;
@@ -2665,6 +2656,73 @@ public class InputManagerService extends IInputManager.Stub
});
}
+ @SuppressLint("MissingPermission")
+ @VisibleForTesting
+ boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event) {
+ int deviceId = event.getDeviceId();
+ boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ switch (event.getKeyGestureType()) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ if (complete) {
+ mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ if (complete) {
+ mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ if (complete) {
+ mNative.toggleCapsLock(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+ if (complete && InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
+ final boolean bounceKeysEnabled =
+ InputSettings.isAccessibilityBounceKeysEnabled(mContext);
+ InputSettings.setAccessibilityBounceKeysThreshold(mContext,
+ bounceKeysEnabled ? 0
+ : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
+ return true;
+ }
+ break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+ if (complete && InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) {
+ final boolean mouseKeysEnabled = InputSettings.isAccessibilityMouseKeysEnabled(
+ mContext);
+ InputSettings.setAccessibilityMouseKeysEnabled(mContext, !mouseKeysEnabled);
+ return true;
+ }
+ break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
+ if (complete && InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+ final boolean stickyKeysEnabled =
+ InputSettings.isAccessibilityStickyKeysEnabled(mContext);
+ InputSettings.setAccessibilityStickyKeysEnabled(mContext, !stickyKeysEnabled);
+ return true;
+ }
+ break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+ if (complete && InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) {
+ final boolean slowKeysEnabled =
+ InputSettings.isAccessibilitySlowKeysEnabled(mContext);
+ InputSettings.setAccessibilitySlowKeysThreshold(mContext,
+ slowKeysEnabled ? 0 : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
+ return true;
+ }
+ break;
+ default:
+ return false;
+
+ }
+ return false;
+ }
+
// Native callback.
@SuppressWarnings("unused")
private void onPointerDownOutsideFocus(IBinder touchedToken) {
@@ -3456,6 +3514,11 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void setDisplayTopology(DisplayTopology topology) {
+ setDisplayTopologyInternal(topology);
+ }
+
+ @Override
public void setDisplayInteractivities(SparseBooleanArray displayInteractivities) {
boolean globallyInteractive = false;
ArraySet<Integer> nonInteractiveDisplays = new ArraySet<>();
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 55d2de2b6865..5f7ad2797368 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -92,6 +92,8 @@ final class KeyGestureController {
| KeyEvent.META_SHIFT_ON;
private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
+ private static final int MSG_PERSIST_CUSTOM_GESTURES = 2;
+ private static final int MSG_LOAD_CUSTOM_GESTURES = 3;
// must match: config_settingsKeyBehavior in config.xml
private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
@@ -116,6 +118,8 @@ final class KeyGestureController {
private final SettingsObserver mSettingsObserver;
private final AppLaunchShortcutManager mAppLaunchShortcutManager;
private final InputGestureManager mInputGestureManager;
+ @GuardedBy("mInputDataStore")
+ private final InputDataStore mInputDataStore;
private static final Object mUserLock = new Object();
@UserIdInt
@GuardedBy("mUserLock")
@@ -155,7 +159,7 @@ final class KeyGestureController {
/** Currently fully consumed key codes per device */
private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
- KeyGestureController(Context context, Looper looper) {
+ KeyGestureController(Context context, Looper looper, InputDataStore inputDataStore) {
mContext = context;
mHandler = new Handler(looper, this::handleMessage);
mSystemPid = Process.myPid();
@@ -175,6 +179,7 @@ final class KeyGestureController {
mSettingsObserver = new SettingsObserver(mHandler);
mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
mInputGestureManager = new InputGestureManager(mContext);
+ mInputDataStore = inputDataStore;
initBehaviors();
initKeyCombinationRules();
}
@@ -434,6 +439,13 @@ final class KeyGestureController {
mSettingsObserver.observe();
mAppLaunchShortcutManager.systemRunning();
mInputGestureManager.systemRunning();
+
+ int userId;
+ synchronized (mUserLock) {
+ userId = mCurrentUserId;
+ }
+ // Load the system user's input gestures.
+ mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -789,7 +801,15 @@ final class KeyGestureController {
+ " interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_DO_NOT_DISTURB:
- // TODO(b/365920375): Implement 25Q2 keycode implementation in system
+ if (enableNew25q2Keycodes()) {
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_DO_NOT_DISTURB},
+ /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
+ /* flags = */0, /* appLaunchData = */null);
+ }
+ }
return true;
}
@@ -955,6 +975,7 @@ final class KeyGestureController {
synchronized (mUserLock) {
mCurrentUserId = userId;
}
+ mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
@MainThread
@@ -995,6 +1016,17 @@ final class KeyGestureController {
AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
notifyKeyGestureEvent(event);
break;
+ case MSG_PERSIST_CUSTOM_GESTURES: {
+ final int userId = (Integer) msg.obj;
+ persistInputGestures(userId);
+ break;
+ }
+ case MSG_LOAD_CUSTOM_GESTURES: {
+ final int userId = (Integer) msg.obj;
+ loadInputGestures(userId);
+ break;
+ }
+
}
return true;
}
@@ -1040,22 +1072,31 @@ final class KeyGestureController {
@InputManager.CustomInputGestureResult
public int addCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- return mInputGestureManager.addCustomInputGesture(userId,
+ final int result = mInputGestureManager.addCustomInputGesture(userId,
new InputGestureData(inputGestureData));
+ if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ }
+ return result;
}
@BinderThread
@InputManager.CustomInputGestureResult
public int removeCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- return mInputGestureManager.removeCustomInputGesture(userId,
+ final int result = mInputGestureManager.removeCustomInputGesture(userId,
new InputGestureData(inputGestureData));
+ if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ }
+ return result;
}
@BinderThread
public void removeAllCustomInputGestures(@UserIdInt int userId,
@Nullable InputGestureData.Filter filter) {
mInputGestureManager.removeAllCustomInputGestures(userId, filter);
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
}
@BinderThread
@@ -1166,6 +1207,26 @@ final class KeyGestureController {
}
}
+ private void persistInputGestures(int userId) {
+ synchronized (mInputDataStore) {
+ final List<InputGestureData> inputGestureDataList =
+ mInputGestureManager.getCustomInputGestures(userId,
+ null);
+ mInputDataStore.saveInputGestures(userId, inputGestureDataList);
+ }
+ }
+
+ private void loadInputGestures(int userId) {
+ synchronized (mInputDataStore) {
+ mInputGestureManager.removeAllCustomInputGestures(userId, null);
+ final List<InputGestureData> inputGestureDataList = mInputDataStore.loadInputGestures(
+ userId);
+ for (final InputGestureData inputGestureData : inputGestureDataList) {
+ mInputGestureManager.addCustomInputGesture(userId, inputGestureData);
+ }
+ }
+ }
+
// A record of a registered key gesture event listener from one process.
private class KeyGestureHandlerRecord implements IBinder.DeathRecipient {
public final int mPid;
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 935f0ffc36ac..c72f7c076a83 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.input;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.display.DisplayTopologyGraph;
import android.hardware.display.DisplayViewport;
import android.hardware.input.InputSensorInfo;
import android.hardware.lights.Light;
@@ -42,6 +43,8 @@ interface NativeInputManagerService {
void setDisplayViewports(DisplayViewport[] viewports);
+ void setDisplayTopology(DisplayTopologyGraph topologyGraph);
+
int getScanCodeState(int deviceId, int sourceMask, int scanCode);
int getKeyCodeState(int deviceId, int sourceMask, int keyCode);
@@ -323,6 +326,9 @@ interface NativeInputManagerService {
public native void setDisplayViewports(DisplayViewport[] viewports);
@Override
+ public native void setDisplayTopology(DisplayTopologyGraph topologyGraph);
+
+ @Override
public native int getScanCodeState(int deviceId, int sourceMask, int scanCode);
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 02dd884ad60d..68001428e567 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1209,8 +1209,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
// Hide soft input before user switch task since switch task may block main handler a while
// and delayed the hideCurrentInputLocked().
final var userData = getUserData(userId);
- hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
- SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+ } else {
+ hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+ }
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
clientToBeReset);
mUserSwitchHandlerTask = task;
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 53389cafecef..4e1df769100b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -18,12 +18,24 @@ package com.android.server.location.contexthub;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.ErrorCode;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubMessage;
-import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubTransactionCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* A class that represents a broker for the endpoint registered by the client app. This class
@@ -31,7 +43,8 @@ import android.hardware.location.IContextHubTransactionCallback;
*
* @hide
*/
-public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
+public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ implements IBinder.DeathRecipient {
private static final String TAG = "ContextHubEndpointBroker";
/** The context of the service. */
@@ -52,18 +65,45 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
/** The remote callback interface for this endpoint. */
private final IContextHubEndpointCallback mContextHubEndpointCallback;
+ /** True if this endpoint is registered with the service. */
+ private AtomicBoolean mIsRegistered = new AtomicBoolean(true);
+
+ private final Object mOpenSessionLock = new Object();
+
+ /** The set of session IDs that are pending remote acceptance */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mPendingSessionIds = new HashSet<>();
+
+ /** The set of session IDs that are actively enabled by this endpoint */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mActiveSessionIds = new HashSet<>();
+
+ /** The set of session IDs that are actively enabled by the remote endpoint */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mActiveRemoteSessionIds = new HashSet<>();
+
+ /** The package name of the app that created the endpoint */
+ private final String mPackageName;
+
+ /* Transaction manager used for sending reliable messages */
+ private final ContextHubTransactionManager mTransactionManager;
+
/* package */ ContextHubEndpointBroker(
Context context,
IContextHubWrapper contextHubProxy,
ContextHubEndpointManager endpointManager,
EndpointInfo halEndpointInfo,
- IContextHubEndpointCallback callback) {
+ IContextHubEndpointCallback callback,
+ String packageName,
+ ContextHubTransactionManager transactionManager) {
mContext = context;
mContextHubProxy = contextHubProxy;
mEndpointManager = endpointManager;
mEndpointInfo = new HubEndpointInfo(halEndpointInfo);
mHalEndpointInfo = halEndpointInfo;
mContextHubEndpointCallback = callback;
+ mPackageName = packageName;
+ mTransactionManager = transactionManager;
}
@Override
@@ -72,37 +112,218 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
}
@Override
- public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo) {
- // TODO(b/378487799): Implement this
- return 0;
- }
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ public int openSession(HubEndpointInfo destination, String serviceDescriptor)
+ throws RemoteException {
+ super.openSession_enforcePermission();
+ if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+ int sessionId = mEndpointManager.reserveSessionId();
+ EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
- @Override
- public void closeSession(int sessionId, int reason) {
- // TODO(b/378487799): Implement this
+ synchronized (mOpenSessionLock) {
+ try {
+ mPendingSessionIds.add(sessionId);
+ mContextHubProxy.openEndpointSession(
+ sessionId,
+ halEndpointInfo.id,
+ mHalEndpointInfo.id,
+ serviceDescriptor);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL openEndpointSession", e);
+ mPendingSessionIds.remove(sessionId);
+ mEndpointManager.returnSessionId(sessionId);
+ throw e;
+ }
+ return sessionId;
+ }
}
@Override
- public void openSessionRequestComplete(int sessionId) {
- // TODO(b/378487799): Implement this
-
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ public void closeSession(int sessionId, int reason) throws RemoteException {
+ super.closeSession_enforcePermission();
+ if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+ try {
+ mContextHubProxy.closeEndpointSession(sessionId, (byte) reason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+ throw e;
+ }
}
@Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public void unregister() {
- // TODO(b/378487799): Implement this
+ super.unregister_enforcePermission();
+ mIsRegistered.set(false);
+ try {
+ mContextHubProxy.unregisterEndpoint(mHalEndpointInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling HAL unregisterEndpoint", e);
+ }
+ synchronized (mOpenSessionLock) {
+ for (int id : mPendingSessionIds) {
+ mEndpointManager.returnSessionId(id);
+ }
+ for (int id : mActiveSessionIds) {
+ mEndpointManager.returnSessionId(id);
+ }
+ mPendingSessionIds.clear();
+ mActiveSessionIds.clear();
+ mActiveRemoteSessionIds.clear();
+ }
+ mEndpointManager.unregisterEndpoint(mEndpointInfo.getIdentifier().getEndpoint());
+ }
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ public void openSessionRequestComplete(int sessionId) {
+ super.openSessionRequestComplete_enforcePermission();
+ synchronized (mOpenSessionLock) {
+ try {
+ mContextHubProxy.endpointSessionOpenComplete(sessionId);
+ mActiveRemoteSessionIds.add(sessionId);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling endpointSessionOpenComplete", e);
+ }
+ }
}
@Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public void sendMessage(
int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
- // TODO(b/381102453): Implement this
+ super.sendMessage_enforcePermission();
+ Message halMessage = ContextHubServiceUtil.createHalMessage(message);
+ synchronized (mOpenSessionLock) {
+ if (!mActiveSessionIds.contains(sessionId)
+ && !mActiveRemoteSessionIds.contains(sessionId)) {
+ throw new SecurityException(
+ "sendMessage called on inactive session (id= " + sessionId + ")");
+ }
+ }
+
+ // TODO(b/381102453): Handle permissions
+ if (callback == null) {
+ try {
+ mContextHubProxy.sendMessageToEndpoint(sessionId, halMessage);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Exception while sending message on session " + sessionId, e);
+ }
+ } else {
+ ContextHubServiceTransaction transaction =
+ mTransactionManager.createSessionMessageTransaction(
+ sessionId, halMessage, mPackageName, callback);
+ try {
+ mTransactionManager.addTransaction(transaction);
+ } catch (IllegalStateException e) {
+ Log.e(
+ TAG,
+ "Unable to add a transaction in sendMessageToEndpoint "
+ + "(session ID = "
+ + sessionId
+ + ")",
+ e);
+ transaction.onTransactionComplete(
+ ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE);
+ }
+ }
}
@Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
- // TODO(b/381102453): Implement this
+ super.sendMessageDeliveryStatus_enforcePermission();
+ MessageDeliveryStatus status = new MessageDeliveryStatus();
+ status.messageSequenceNumber = messageSeqNumber;
+ status.errorCode = errorCode;
+ try {
+ mContextHubProxy.sendMessageDeliveryStatusToEndpoint(sessionId, status);
+ } catch (RemoteException e) {
+ Log.w(
+ TAG,
+ "Exception while sending message delivery status on session " + sessionId,
+ e);
+ }
+ }
+
+ /** Invoked when the underlying binder of this broker has died at the client process. */
+ @Override
+ public void binderDied() {
+ if (mIsRegistered.get()) {
+ unregister();
+ }
+ }
+
+ /* package */ void attachDeathRecipient() throws RemoteException {
+ if (mContextHubEndpointCallback != null) {
+ mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
+ }
+ }
+
+ /* package */ void onEndpointSessionOpenRequest(
+ int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionOpenRequest(
+ sessionId, initiator, serviceDescriptor);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionOpenRequest", e);
+ }
+ }
+ }
+
+ /* package */ void onCloseEndpointSession(int sessionId, byte reason) {
+ synchronized (mOpenSessionLock) {
+ mPendingSessionIds.remove(sessionId);
+ mActiveSessionIds.remove(sessionId);
+ mActiveRemoteSessionIds.remove(sessionId);
+ }
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionClosed(
+ sessionId, ContextHubServiceUtil.toAppHubEndpointReason(reason));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionClosed", e);
+ }
+ }
+ }
+
+ /* package */ void onEndpointSessionOpenComplete(int sessionId) {
+ synchronized (mOpenSessionLock) {
+ mPendingSessionIds.remove(sessionId);
+ mActiveSessionIds.add(sessionId);
+ }
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionOpenComplete(sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionClosed", e);
+ }
+ }
+ }
+
+ /* package */ void onMessageReceived(int sessionId, HubMessage message) {
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onMessageReceived(sessionId, message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onMessageReceived", e);
+ }
+ }
+ }
+
+ /* package */ void onMessageDeliveryStatusReceived(
+ int sessionId, int sequenceNumber, byte errorCode) {
+ mTransactionManager.onMessageDeliveryResponse(sequenceNumber, errorCode == ErrorCode.OK);
+ }
+
+ /* package */ boolean hasSessionId(int sessionId) {
+ synchronized (mOpenSessionLock) {
+ return mPendingSessionIds.contains(sessionId)
+ || mActiveSessionIds.contains(sessionId)
+ || mActiveRemoteSessionIds.contains(sessionId);
+ }
}
}
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 54ce74f7b2f1..07df7f994b24 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -19,14 +19,19 @@ package com.android.server.location.contexthub;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -34,30 +39,99 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @hide
*/
-/* package */ class ContextHubEndpointManager {
+/* package */ class ContextHubEndpointManager
+ implements ContextHubHalEndpointCallback.IEndpointSessionCallback {
private static final String TAG = "ContextHubEndpointManager";
/** The hub ID of the Context Hub Service. */
private static final long SERVICE_HUB_ID = 0x416e64726f696400L;
+ /** The range of session IDs to use for endpoints */
+ private static final int SERVICE_SESSION_RANGE = 1024;
+
+ /** The length of the array that should be returned by HAL requestSessionIdRange */
+ private static final int SERVICE_SESSION_RANGE_LENGTH = 2;
+
/** The context of the service. */
private final Context mContext;
/** The proxy to talk to the Context Hub. */
private final IContextHubWrapper mContextHubProxy;
+ private final HubInfoRegistry mHubInfoRegistry;
+
+ private final ContextHubTransactionManager mTransactionManager;
+
/** A map of endpoint IDs to brokers currently registered. */
private final Map<Long, ContextHubEndpointBroker> mEndpointMap = new ConcurrentHashMap<>();
/** Variables for managing endpoint ID creation */
private final Object mEndpointLock = new Object();
+ /**
+ * The next available endpoint ID to register. Per EndpointId.aidl definition, dynamic
+ * endpoint IDs must have the left-most bit as 1, and the values 0/-1 are invalid.
+ */
@GuardedBy("mEndpointLock")
- private long mNextEndpointId = 0;
+ private long mNextEndpointId = -2;
+
+ /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+ private final int mMinSessionId;
+
+ /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+ private final int mMaxSessionId;
+
+ /** Variables for managing session ID creation */
+ private final Object mSessionIdLock = new Object();
- /* package */ ContextHubEndpointManager(Context context, IContextHubWrapper contextHubProxy) {
+ /** A set of session IDs that have been reserved by an endpoint. */
+ @GuardedBy("mSessionIdLock")
+ private final Set<Integer> mReservedSessionIds =
+ Collections.newSetFromMap(new HashMap<Integer, Boolean>());
+
+ @GuardedBy("mSessionIdLock")
+ private int mNextSessionId = 0;
+
+ /** Initialized to true if all initialization in the constructor succeeds. */
+ private final boolean mSessionIdsValid;
+
+ /* package */ ContextHubEndpointManager(
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ HubInfoRegistry hubInfoRegistry,
+ ContextHubTransactionManager transactionManager) {
mContext = context;
mContextHubProxy = contextHubProxy;
+ mHubInfoRegistry = hubInfoRegistry;
+ mTransactionManager = transactionManager;
+ int[] range = null;
+ try {
+ range = mContextHubProxy.requestSessionIdRange(SERVICE_SESSION_RANGE);
+ if (range != null && range.length < SERVICE_SESSION_RANGE_LENGTH) {
+ Log.e(TAG, "Invalid session ID range: range array size = " + range.length);
+ range = null;
+ }
+ } catch (RemoteException | IllegalArgumentException | ServiceSpecificException e) {
+ Log.e(TAG, "Exception while calling HAL requestSessionIdRange", e);
+ }
+
+ if (range == null) {
+ mMinSessionId = -1;
+ mMaxSessionId = -1;
+ mSessionIdsValid = false;
+ } else {
+ mMinSessionId = range[0];
+ mMaxSessionId = range[1];
+ if (!isSessionIdRangeValid(mMinSessionId, mMaxSessionId)) {
+ Log.e(
+ TAG,
+ "Invalid session ID range: max=" + mMaxSessionId + " min=" + mMinSessionId);
+ mSessionIdsValid = false;
+ } else {
+ mNextSessionId = mMinSessionId;
+ mSessionIdsValid = true;
+ }
+ }
}
/**
@@ -65,12 +139,18 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @param pendingEndpointInfo the object describing the endpoint being registered
* @param callback the callback interface of the endpoint to register
+ * @param packageName the name of the package of the calling client
* @return the endpoint interface
* @throws IllegalStateException if max number of endpoints have already registered
*/
/* package */ IContextHubEndpoint registerEndpoint(
- HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
+ HubEndpointInfo pendingEndpointInfo,
+ IContextHubEndpointCallback callback,
+ String packageName)
throws RemoteException {
+ if (!mSessionIdsValid) {
+ throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
+ }
ContextHubEndpointBroker broker;
long endpointId = getNewEndpointId();
EndpointInfo halEndpointInfo =
@@ -88,24 +168,179 @@ import java.util.concurrent.ConcurrentHashMap;
mContextHubProxy,
this /* endpointManager */,
halEndpointInfo,
- callback);
+ callback,
+ packageName,
+ mTransactionManager);
mEndpointMap.put(endpointId, broker);
- // TODO(b/378487799): Add death recipient
+ try {
+ broker.attachDeathRecipient();
+ } catch (RemoteException e) {
+ // The client process has died, so we close the connection and return null
+ Log.e(TAG, "Failed to attach death recipient to client", e);
+ broker.unregister();
+ return null;
+ }
Log.d(TAG, "Registered endpoint with ID = " + endpointId);
return IContextHubEndpoint.Stub.asInterface(broker);
}
/**
- * @return an available endpoint ID
+ * Reserves an available session ID for an endpoint.
+ *
+ * @throws IllegalStateException if no session ID was available.
+ * @return The reserved session ID.
+ */
+ /* package */ int reserveSessionId() {
+ int id = -1;
+ synchronized (mSessionIdLock) {
+ final int maxCapacity = mMaxSessionId - mMinSessionId + 1;
+ if (mReservedSessionIds.size() >= maxCapacity) {
+ throw new IllegalStateException("Too many sessions reserved");
+ }
+
+ id = mNextSessionId;
+ for (int i = mMinSessionId; i <= mMaxSessionId; i++) {
+ if (!mReservedSessionIds.contains(id)) {
+ mNextSessionId = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+ break;
+ }
+
+ id = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+ }
+
+ mReservedSessionIds.add(id);
+ }
+ return id;
+ }
+
+ /** Returns a previously reserved session ID through {@link #reserveSessionId()}. */
+ /* package */ void returnSessionId(int sessionId) {
+ synchronized (mSessionIdLock) {
+ mReservedSessionIds.remove(sessionId);
+ }
+ }
+
+ /**
+ * Unregisters an endpoint given its ID.
+ *
+ * @param endpointId The ID of the endpoint to unregister.
*/
+ /* package */ void unregisterEndpoint(long endpointId) {
+ mEndpointMap.remove(endpointId);
+ }
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destination,
+ HubEndpointInfo.HubEndpointIdentifier initiator,
+ String serviceDescriptor) {
+ if (destination.getHub() != SERVICE_HUB_ID) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: invalid destination hub ID: "
+ + destination.getHub());
+ return;
+ }
+ ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
+ if (broker == null) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: unknown destination endpoint ID: "
+ + destination.getEndpoint());
+ return;
+ }
+ HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
+ if (initiatorInfo == null) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
+ + initiator.getEndpoint());
+ return;
+ }
+ broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ }
+
+ @Override
+ public void onCloseEndpointSession(int sessionId, byte reason) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onCloseEndpointSession(sessionId, reason);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onCloseEndpointSession: unknown session ID " + sessionId);
+ }
+ }
+
+ @Override
+ public void onEndpointSessionOpenComplete(int sessionId) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onEndpointSessionOpenComplete(sessionId);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onEndpointSessionOpenComplete: unknown session ID " + sessionId);
+ }
+ }
+
+ @Override
+ public void onMessageReceived(int sessionId, HubMessage message) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onMessageReceived(sessionId, message);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onMessageReceived: unknown session ID " + sessionId);
+ }
+ }
+
+ @Override
+ public void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onMessageDeliveryStatusReceived(sessionId, sequenceNumber, errorCode);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onMessageDeliveryStatusReceived: unknown session ID " + sessionId);
+ }
+ }
+
+ /** @return an available endpoint ID */
private long getNewEndpointId() {
synchronized (mEndpointLock) {
- if (mNextEndpointId == Long.MAX_VALUE) {
+ if (mNextEndpointId >= 0) {
throw new IllegalStateException("Too many endpoints connected");
}
- return mNextEndpointId++;
+ return mNextEndpointId--;
}
}
+
+ /**
+ * @return true if the provided session ID range is valid
+ */
+ private boolean isSessionIdRangeValid(int minId, int maxId) {
+ return (minId <= maxId) && (minId >= 0) && (maxId >= 0);
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
index c05f7a0c0e00..f1f2217fc3c4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
@@ -17,6 +17,7 @@ package com.android.server.location.contexthub;
import android.hardware.contexthub.EndpointId;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
import android.hardware.contexthub.IEndpointCallback;
import android.hardware.contexthub.Message;
import android.hardware.contexthub.MessageDeliveryStatus;
@@ -26,6 +27,7 @@ import android.os.RemoteException;
public class ContextHubHalEndpointCallback
extends android.hardware.contexthub.IEndpointCallback.Stub {
private final IEndpointLifecycleCallback mEndpointLifecycleCallback;
+ private final IEndpointSessionCallback mEndpointSessionCallback;
/** Interface for listening for endpoint start and stop events. */
public interface IEndpointLifecycleCallback {
@@ -36,8 +38,33 @@ public class ContextHubHalEndpointCallback
void onEndpointStopped(HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason);
}
- ContextHubHalEndpointCallback(IEndpointLifecycleCallback endpointLifecycleCallback) {
+ /** Interface for listening for endpoint session events. */
+ public interface IEndpointSessionCallback {
+ /** Called when an endpoint session open is requested by the HAL. */
+ void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destinationId,
+ HubEndpointInfo.HubEndpointIdentifier initiatorId,
+ String serviceDescriptor);
+
+ /** Called when a endpoint close session is completed. */
+ void onCloseEndpointSession(int sessionId, byte reason);
+
+ /** Called when a requested endpoint open session is completed */
+ void onEndpointSessionOpenComplete(int sessionId);
+
+ /** Called when a message is received for the session */
+ void onMessageReceived(int sessionId, HubMessage message);
+
+ /** Called when a message delivery status is received for the session */
+ void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode);
+ }
+
+ ContextHubHalEndpointCallback(
+ IEndpointLifecycleCallback endpointLifecycleCallback,
+ IEndpointSessionCallback endpointSessionCallback) {
mEndpointLifecycleCallback = endpointLifecycleCallback;
+ mEndpointSessionCallback = endpointSessionCallback;
}
@Override
@@ -48,7 +75,7 @@ public class ContextHubHalEndpointCallback
}
HubEndpointInfo[] endpointInfos = new HubEndpointInfo[halEndpointInfos.length];
for (int i = 0; i < halEndpointInfos.length; i++) {
- endpointInfos[i++] = new HubEndpointInfo(halEndpointInfos[i]);
+ endpointInfos[i] = new HubEndpointInfo(halEndpointInfos[i]);
}
mEndpointLifecycleCallback.onEndpointStarted(endpointInfos);
}
@@ -64,22 +91,37 @@ public class ContextHubHalEndpointCallback
}
@Override
- public void onMessageReceived(int i, Message message) throws RemoteException {}
+ public void onEndpointSessionOpenRequest(
+ int i, EndpointId destination, EndpointId initiator, String s) throws RemoteException {
+ HubEndpointInfo.HubEndpointIdentifier destinationId =
+ new HubEndpointInfo.HubEndpointIdentifier(destination.hubId, destination.id);
+ HubEndpointInfo.HubEndpointIdentifier initiatorId =
+ new HubEndpointInfo.HubEndpointIdentifier(initiator.hubId, initiator.id);
+ mEndpointSessionCallback.onEndpointSessionOpenRequest(i, destinationId, initiatorId, s);
+ }
@Override
- public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
- throws RemoteException {}
+ public void onCloseEndpointSession(int i, byte b) throws RemoteException {
+ mEndpointSessionCallback.onCloseEndpointSession(i, b);
+ }
@Override
- public void onEndpointSessionOpenRequest(
- int i, EndpointId endpointId, EndpointId endpointId1, String s)
- throws RemoteException {}
+ public void onEndpointSessionOpenComplete(int i) throws RemoteException {
+ mEndpointSessionCallback.onEndpointSessionOpenComplete(i);
+ }
@Override
- public void onCloseEndpointSession(int i, byte b) throws RemoteException {}
+ public void onMessageReceived(int i, Message message) throws RemoteException {
+ HubMessage hubMessage = ContextHubServiceUtil.createHubMessage(message);
+ mEndpointSessionCallback.onMessageReceived(i, hubMessage);
+ }
@Override
- public void onEndpointSessionOpenComplete(int i) throws RemoteException {}
+ public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
+ throws RemoteException {
+ mEndpointSessionCallback.onMessageDeliveryStatusReceived(
+ i, messageDeliveryStatus.messageSequenceNumber, messageDeliveryStatus.errorCode);
+ }
@Override
public int getInterfaceVersion() throws RemoteException {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 0d926b99217d..165f9d3340e3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -333,7 +333,9 @@ public class ContextHubService extends IContextHubService.Stub {
HubInfoRegistry registry;
try {
registry = new HubInfoRegistry(mContextHubWrapper);
- mEndpointManager = new ContextHubEndpointManager(mContext, mContextHubWrapper);
+ mEndpointManager =
+ new ContextHubEndpointManager(
+ mContext, mContextHubWrapper, registry, mTransactionManager);
Log.i(TAG, "Enabling generic offload API");
} catch (UnsupportedOperationException e) {
mEndpointManager = null;
@@ -533,7 +535,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
try {
mContextHubWrapper.registerEndpointCallback(
- new ContextHubHalEndpointCallback(mHubInfoRegistry));
+ new ContextHubHalEndpointCallback(mHubInfoRegistry, mEndpointManager));
} catch (RemoteException | UnsupportedOperationException e) {
Log.e(TAG, "Exception while registering IEndpointCallback", e);
}
@@ -793,14 +795,16 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public IContextHubEndpoint registerEndpoint(
- HubEndpointInfo pendingHubEndpointInfo, IContextHubEndpointCallback callback)
+ HubEndpointInfo pendingHubEndpointInfo,
+ IContextHubEndpointCallback callback,
+ String packageName)
throws RemoteException {
super.registerEndpoint_enforcePermission();
if (mEndpointManager == null) {
- Log.e(TAG, "ContextHubService.registerEndpoint: endpoint manager failed to initialize");
+ Log.e(TAG, "Endpoint manager failed to initialize");
throw new UnsupportedOperationException("Endpoint registration is not supported");
}
- return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
+ return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback, packageName);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -808,7 +812,8 @@ public class ContextHubService extends IContextHubService.Stub {
public void registerEndpointDiscoveryCallbackId(
long endpointId, IContextHubEndpointDiscoveryCallback callback) throws RemoteException {
super.registerEndpointDiscoveryCallbackId_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.registerEndpointDiscoveryCallback(endpointId, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -817,7 +822,8 @@ public class ContextHubService extends IContextHubService.Stub {
String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback)
throws RemoteException {
super.registerEndpointDiscoveryCallbackDescriptor_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.registerEndpointDiscoveryCallback(serviceDescriptor, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -825,7 +831,15 @@ public class ContextHubService extends IContextHubService.Stub {
public void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback)
throws RemoteException {
super.unregisterEndpointDiscoveryCallback_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.unregisterEndpointDiscoveryCallback(callback);
+ }
+
+ private void checkEndpointDiscoveryPreconditions() {
+ if (mHubInfoRegistry == null) {
+ Log.e(TAG, "Hub endpoint registry failed to initialize");
+ throw new UnsupportedOperationException("Endpoint discovery is not supported");
+ }
}
/**
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
index 3aea6d533295..4e96b4442a33 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.chre.flags.Flags;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.NanoAppState;
@@ -46,7 +47,11 @@ abstract class ContextHubServiceTransaction {
/** The number of times the transaction has been started (start function called). */
private int mNumCompletedStartCalls;
- private final short mHostEndpointId;
+ /**
+ * A unique identifier for the entity which owns this transaction, scoped by the transaction
+ * type.
+ */
+ private final int mOwnerId;
private boolean mIsComplete = false;
@@ -59,7 +64,7 @@ abstract class ContextHubServiceTransaction {
mNextRetryTime = Long.MAX_VALUE;
mTimeoutTime = Long.MAX_VALUE;
mNumCompletedStartCalls = 0;
- mHostEndpointId = Short.MAX_VALUE;
+ mOwnerId = Integer.MAX_VALUE;
}
ContextHubServiceTransaction(int id, int type, long nanoAppId,
@@ -72,11 +77,11 @@ abstract class ContextHubServiceTransaction {
mNextRetryTime = Long.MAX_VALUE;
mTimeoutTime = Long.MAX_VALUE;
mNumCompletedStartCalls = 0;
- mHostEndpointId = Short.MAX_VALUE;
+ mOwnerId = Integer.MAX_VALUE;
}
- ContextHubServiceTransaction(int id, int type, String packageName,
- int messageSequenceNumber, short hostEndpointId) {
+ ContextHubServiceTransaction(
+ int id, int type, String packageName, int messageSequenceNumber, int ownerId) {
mTransactionId = id;
mTransactionType = type;
mNanoAppId = Long.MAX_VALUE;
@@ -85,7 +90,7 @@ abstract class ContextHubServiceTransaction {
mNextRetryTime = Long.MAX_VALUE;
mTimeoutTime = Long.MAX_VALUE;
mNumCompletedStartCalls = 0;
- mHostEndpointId = hostEndpointId;
+ mOwnerId = ownerId;
}
/**
@@ -147,8 +152,15 @@ abstract class ContextHubServiceTransaction {
return mNumCompletedStartCalls;
}
- short getHostEndpointId() {
- return mHostEndpointId;
+ /**
+ * @return A unique identifier for the entity owning this transaction.
+ */
+ long getOwnerId() {
+ if (Flags.offloadImplementation()) {
+ return ((long) mTransactionType << 32) | (0x00000000FFFFFFFFL & mOwnerId);
+ } else {
+ return mOwnerId;
+ }
}
/**
@@ -215,15 +227,16 @@ abstract class ContextHubServiceTransaction {
out.append(", messageSequenceNumber = ");
out.append(mMessageSequenceNumber);
}
- if (mTransactionType == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+ if (mTransactionType == ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+ || mTransactionType == ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE) {
out.append(", nextRetryTime = ");
out.append(mNextRetryTime);
out.append(", timeoutTime = ");
out.append(mTimeoutTime);
out.append(", numCompletedStartCalls = ");
out.append(mNumCompletedStartCalls);
- out.append(", hostEndpointId = ");
- out.append(mHostEndpointId);
+ out.append(", ownerId = ");
+ out.append(getOwnerId());
}
out.append(")");
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 05be427f9daf..77ec51af80a7 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -19,8 +19,12 @@ package com.android.server.location.contexthub;
import android.Manifest;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpoint;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.Reason;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
@@ -465,4 +469,70 @@ import java.util.List;
}
return outputInfo;
}
+
+ /**
+ * Converts a HubMessage object to a AIDL HAL Message object.
+ *
+ * @param message the HubMessage message to convert
+ * @return the AIDL HAL message
+ */
+ /* package */
+ static Message createHalMessage(HubMessage message) {
+ Message outMessage = new Message();
+ outMessage.flags =
+ message.getDeliveryParams().isResponseRequired()
+ ? Message.FLAG_REQUIRES_DELIVERY_STATUS
+ : 0;
+ outMessage.permissions = new String[0];
+ outMessage.sequenceNumber = message.getMessageSequenceNumber();
+ outMessage.type = message.getMessageType();
+ outMessage.content = message.getMessageBody();
+ return outMessage;
+ }
+
+ /**
+ * Converts a AIDL HAL Message object to a HubMessage object.
+ *
+ * @param message the AIDL HAL Message message to convert
+ * @return the HubMessage
+ */
+ /* package */
+ static HubMessage createHubMessage(Message message) {
+ boolean isReliable = (message.flags & Message.FLAG_REQUIRES_DELIVERY_STATUS) != 0;
+ return HubMessage.createMessage(
+ message.type,
+ message.content,
+ HubMessage.DeliveryParams.makeBasic().setResponseRequired(isReliable));
+ }
+
+ /**
+ * Converts a byte integer defined by Reason.aidl to HubEndpoint.Reason values exposed to apps.
+ *
+ * @param reason The Reason.aidl value
+ * @return The converted HubEndpoint.Reason value
+ */
+ /* package */
+ static @HubEndpoint.Reason int toAppHubEndpointReason(byte reason) {
+ switch (reason) {
+ case Reason.UNSPECIFIED:
+ case Reason.OUT_OF_MEMORY:
+ case Reason.TIMEOUT:
+ return HubEndpoint.REASON_FAILURE;
+ case Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED:
+ return HubEndpoint.REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED;
+ case Reason.CLOSE_ENDPOINT_SESSION_REQUESTED:
+ return HubEndpoint.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED;
+ case Reason.ENDPOINT_INVALID:
+ return HubEndpoint.REASON_ENDPOINT_INVALID;
+ case Reason.ENDPOINT_GONE:
+ case Reason.ENDPOINT_CRASHED:
+ case Reason.HUB_RESET:
+ return HubEndpoint.REASON_ENDPOINT_STOPPED;
+ case Reason.PERMISSION_DENIED:
+ return HubEndpoint.REASON_PERMISSION_DENIED;
+ default:
+ Log.w(TAG, "toAppHubEndpointReason: invalid reason: " + reason);
+ return HubEndpoint.REASON_FAILURE;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index ccfa61b400b6..5dd40ea97a64 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -17,6 +17,7 @@
package com.android.server.location.contexthub;
import android.chre.flags.Flags;
+import android.hardware.contexthub.Message;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoAppBinary;
@@ -84,9 +85,9 @@ import java.util.concurrent.atomic.AtomicInteger;
protected final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
new HashMap<>();
- /** A set of host endpoint IDs that have an active pending transaction. */
+ /** A set of IDs of transaction owners that have an active pending transaction. */
@GuardedBy("mReliableMessageLock")
- protected final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
+ protected final Set<Long> mReliableMessageOwnerIdActiveSet = new HashSet<>();
protected final AtomicInteger mNextAvailableId = new AtomicInteger();
@@ -355,27 +356,32 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Creates a transaction to send a reliable message.
*
- * @param hostEndpointId The ID of the host endpoint sending the message.
- * @param contextHubId The ID of the hub to send the message to.
- * @param message The message to send.
+ * @param ownerId The ID of the transaction owner.
+ * @param contextHubId The ID of the hub to send the message to.
+ * @param message The message to send.
* @param transactionCallback The callback of the transactions.
- * @param packageName The host package associated with this transaction.
+ * @param packageName The host package associated with this transaction.
* @return The generated transaction.
*/
/* package */ ContextHubServiceTransaction createMessageTransaction(
- short hostEndpointId, int contextHubId, NanoAppMessage message,
- IContextHubTransactionCallback transactionCallback, String packageName) {
- return new ContextHubServiceTransaction(mNextAvailableId.getAndIncrement(),
- ContextHubTransaction.TYPE_RELIABLE_MESSAGE, packageName,
- mNextAvailableMessageSequenceNumber.getAndIncrement(), hostEndpointId) {
+ short ownerId,
+ int contextHubId,
+ NanoAppMessage message,
+ IContextHubTransactionCallback transactionCallback,
+ String packageName) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(),
+ ContextHubTransaction.TYPE_RELIABLE_MESSAGE,
+ packageName,
+ mNextAvailableMessageSequenceNumber.getAndIncrement(),
+ ownerId) {
@Override
/* package */ int onTransact() {
try {
message.setIsReliable(/* isReliable= */ true);
message.setMessageSequenceNumber(getMessageSequenceNumber());
- return mContextHubProxy.sendMessageToContextHub(hostEndpointId, contextHubId,
- message);
+ return mContextHubProxy.sendMessageToContextHub(ownerId, contextHubId, message);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to send a reliable message", e);
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
@@ -394,6 +400,48 @@ import java.util.concurrent.atomic.AtomicInteger;
}
/**
+ * Creates a transaction to send a message through a session.
+ *
+ * @param sessionId The ID of the endpoint session the message should be sent through.
+ * @param message The message to send.
+ * @param transactionCallback The callback of the transactions.
+ * @return The generated transaction.
+ */
+ /* package */ ContextHubServiceTransaction createSessionMessageTransaction(
+ int sessionId,
+ Message message,
+ String packageName,
+ IContextHubTransactionCallback transactionCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(),
+ ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE,
+ packageName,
+ mNextAvailableMessageSequenceNumber.getAndIncrement(),
+ sessionId) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ message.sequenceNumber = getMessageSequenceNumber();
+ mContextHubProxy.sendMessageToEndpoint(sessionId, message);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to send a session message", e);
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ try {
+ transactionCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+ }
+ }
+ };
+ }
+
+ /**
* Creates a transaction for querying for a list of nanoapps.
*
* @param contextHubId the ID of the hub to query
@@ -452,9 +500,14 @@ import java.util.concurrent.atomic.AtomicInteger;
mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
}
- if (Flags.reliableMessageRetrySupportService()
- && transaction.getTransactionType()
- == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+ boolean isReliableMessage =
+ Flags.reliableMessageRetrySupportService()
+ && (transaction.getTransactionType()
+ == ContextHubTransaction.TYPE_RELIABLE_MESSAGE);
+ boolean isEndpointMessage =
+ (transaction.getTransactionType()
+ == ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE);
+ if (isReliableMessage || isEndpointMessage) {
synchronized (mReliableMessageLock) {
if (mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
throw new IllegalStateException(
@@ -766,10 +819,10 @@ import java.util.concurrent.atomic.AtomicInteger;
mReliableMessageTransactionMap.entrySet().iterator();
while (iter.hasNext()) {
ContextHubServiceTransaction transaction = iter.next().getValue();
- short hostEndpointId = transaction.getHostEndpointId();
+ long ownerId = transaction.getOwnerId();
int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
if (numCompletedStartCalls == 0
- && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+ && mReliableMessageOwnerIdActiveSet.contains(ownerId)) {
continue;
}
@@ -871,7 +924,7 @@ import java.util.concurrent.atomic.AtomicInteger;
} else {
iter.remove();
}
- mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+ mReliableMessageOwnerIdActiveSet.remove(transaction.getOwnerId());
}
/**
@@ -906,7 +959,7 @@ import java.util.concurrent.atomic.AtomicInteger;
transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
}
transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
- mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+ mReliableMessageOwnerIdActiveSet.add(transaction.getOwnerId());
}
private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
index a67fa308a6ea..657375d2cbd7 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
@@ -18,28 +18,14 @@ package com.android.server.location.contexthub;
import android.chre.flags.Flags;
import android.hardware.location.ContextHubTransaction;
-import android.hardware.location.IContextHubTransactionCallback;
-import android.hardware.location.NanoAppBinary;
-import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-import java.time.Duration;
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages transactions at the Context Hub Service.
@@ -326,10 +312,10 @@ import java.util.concurrent.atomic.AtomicInteger;
mReliableMessageTransactionMap.entrySet().iterator();
while (iter.hasNext()) {
ContextHubServiceTransaction transaction = iter.next().getValue();
- short hostEndpointId = transaction.getHostEndpointId();
+ long ownerId = transaction.getOwnerId();
int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
if (numCompletedStartCalls == 0
- && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+ && mReliableMessageOwnerIdActiveSet.contains(ownerId)) {
continue;
}
@@ -394,7 +380,7 @@ import java.util.concurrent.atomic.AtomicInteger;
} else {
iter.remove();
}
- mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+ mReliableMessageOwnerIdActiveSet.remove(transaction.getOwnerId());
Log.d(
TAG,
@@ -436,7 +422,7 @@ import java.util.concurrent.atomic.AtomicInteger;
transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
}
transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
- mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+ mReliableMessageOwnerIdActiveSet.add(transaction.getOwnerId());
}
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index d2b2331d54f3..503f1aca64d9 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -18,7 +18,9 @@ package com.android.server.location.contexthub;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
import android.hardware.location.HubInfo;
+import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -29,6 +31,9 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BiConsumer;
class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycleCallback {
private static final String TAG = "HubInfoRegistry";
@@ -43,6 +48,56 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
private final ArrayMap<HubEndpointInfo.HubEndpointIdentifier, HubEndpointInfo>
mHubEndpointInfos = new ArrayMap<>();
+ /**
+ * A wrapper class that is used to store arguments to
+ * ContextHubManager.registerEndpointCallback.
+ */
+ private static class DiscoveryCallback {
+ private final IContextHubEndpointDiscoveryCallback mCallback;
+ private final Optional<Long> mEndpointId;
+ private final Optional<String> mServiceDescriptor;
+
+ DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, long endpointId) {
+ mCallback = callback;
+ mEndpointId = Optional.of(endpointId);
+ mServiceDescriptor = Optional.empty();
+ }
+
+ DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, String serviceDescriptor) {
+ mCallback = callback;
+ mEndpointId = Optional.empty();
+ mServiceDescriptor = Optional.of(serviceDescriptor);
+ }
+
+ public IContextHubEndpointDiscoveryCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * @param info The hub endpoint info to check
+ * @return true if info matches
+ */
+ public boolean isMatch(HubEndpointInfo info) {
+ if (mEndpointId.isPresent()) {
+ return mEndpointId.get() == info.getIdentifier().getEndpoint();
+ }
+ if (mServiceDescriptor.isPresent()) {
+ for (HubServiceInfo serviceInfo : info.getServiceInfoCollection()) {
+ if (mServiceDescriptor.get().equals(serviceInfo.getServiceDescriptor())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /* The list of discovery callbacks registered with the service */
+ @GuardedBy("mCallbackLock")
+ private final List<DiscoveryCallback> mEndpointDiscoveryCallbacks = new ArrayList<>();
+
+ private final Object mCallbackLock = new Object();
+
HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
mContextHubWrapper = contextHubWrapper;
refreshCachedHubs();
@@ -87,6 +142,12 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
}
}
+ public HubEndpointInfo getEndpointInfo(HubEndpointInfo.HubEndpointIdentifier id) {
+ synchronized (mLock) {
+ return mHubEndpointInfos.get(id);
+ }
+ }
+
/** Invoked when HAL restarts */
public void onHalRestart() {
synchronized (mLock) {
@@ -103,16 +164,51 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
mHubEndpointInfos.put(endpointInfo.getIdentifier(), endpointInfo);
}
}
+
+ invokeForMatchingEndpoints(
+ endpointInfos,
+ (cb, infoList) -> {
+ try {
+ cb.onEndpointsStarted(infoList);
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "onEndpointStarted: callback died, unregistering");
+ unregisterEndpointDiscoveryCallback(cb);
+ } else {
+ Log.e(TAG, "Exception while calling onEndpointsStarted", e);
+ }
+ }
+ });
}
@Override
public void onEndpointStopped(
HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason) {
+ ArrayList<HubEndpointInfo> removedInfoList = new ArrayList<>();
synchronized (mLock) {
for (HubEndpointInfo.HubEndpointIdentifier endpointId : endpointIds) {
- mHubEndpointInfos.remove(endpointId);
+ HubEndpointInfo info = mHubEndpointInfos.remove(endpointId);
+ if (info != null) {
+ removedInfoList.add(info);
+ }
}
}
+
+ invokeForMatchingEndpoints(
+ removedInfoList.toArray(new HubEndpointInfo[removedInfoList.size()]),
+ (cb, infoList) -> {
+ try {
+ cb.onEndpointsStopped(
+ infoList, ContextHubServiceUtil.toAppHubEndpointReason(reason));
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "onEndpointStopped: callback died, unregistering");
+ unregisterEndpointDiscoveryCallback(cb);
+ } else {
+ Log.e(TAG, "Exception while calling onEndpointsStopped", e);
+ }
+ }
+ });
}
/** Return a list of {@link HubEndpointInfo} that represents endpoints with the matching id. */
@@ -145,6 +241,77 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
return searchResult;
}
+ /* package */
+ void registerEndpointDiscoveryCallback(
+ long endpointId, IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ checkCallbackAlreadyRegistered(callback);
+ mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, endpointId));
+ }
+ }
+
+ /* package */
+ void registerEndpointDiscoveryCallback(
+ String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ checkCallbackAlreadyRegistered(callback);
+ mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, serviceDescriptor));
+ }
+ }
+
+ /* package */
+ void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ if (discoveryCallback.getCallback().asBinder() == callback.asBinder()) {
+ mEndpointDiscoveryCallbacks.remove(discoveryCallback);
+ break;
+ }
+ }
+ }
+ }
+
+ private void checkCallbackAlreadyRegistered(
+ IContextHubEndpointDiscoveryCallback callback) {
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ if (discoveryCallback.mCallback.asBinder() == callback.asBinder()) {
+ throw new IllegalArgumentException("Callback is already registered");
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates through all registered discovery callbacks and invokes a given callback for those
+ * that match the endpoints the callback is targeted for.
+ *
+ * @param endpointInfos The list of endpoint infos to check for a match.
+ * @param consumer The callback to invoke, which consumes the callback object and the list of
+ * matched endpoint infos.
+ */
+ private void invokeForMatchingEndpoints(
+ HubEndpointInfo[] endpointInfos,
+ BiConsumer<IContextHubEndpointDiscoveryCallback, HubEndpointInfo[]> consumer) {
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ ArrayList<HubEndpointInfo> infoList = new ArrayList<>();
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ if (discoveryCallback.isMatch(endpointInfo)) {
+ infoList.add(endpointInfo);
+ }
+ }
+
+ consumer.accept(
+ discoveryCallback.getCallback(),
+ infoList.toArray(new HubEndpointInfo[infoList.size()]));
+ }
+ }
+ }
+
void dump(IndentingPrintWriter ipw) {
synchronized (mLock) {
dumpLocked(ipw);
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 14d75b02d6b0..e1df503eccdb 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,8 +18,10 @@ package com.android.server.location.contexthub;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.chre.flags.Flags;
+import android.hardware.contexthub.EndpointId;
import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.Message;
import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.contexthub.NanSessionRequest;
import android.hardware.contexthub.V1_0.ContextHub;
@@ -243,6 +245,33 @@ public abstract class IContextHubWrapper {
public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
throws RemoteException {}
+ /** Unregisters a previously registered endpoint */
+ public int[] requestSessionIdRange(int size) throws RemoteException {
+ return null;
+ }
+
+ /** Opens an endpoint session between two endpoints */
+ public void openEndpointSession(
+ int sessionId, EndpointId destination, EndpointId initiator, String serviceDescriptor)
+ throws RemoteException {}
+
+ /** Closes a previously opened endpoint */
+ public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {}
+
+ /** Unregisters a previously registered endpoint */
+ public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {}
+
+ /** Notifies the completion of a session opened by the HAL */
+ public void endpointSessionOpenComplete(int sessionId) throws RemoteException {}
+
+ /** Sends a message to a remote endpoint */
+ public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {}
+
+ /** Sends a message delivery status to a remote endpoint */
+ public void sendMessageDeliveryStatusToEndpoint(int sessionId, MessageDeliveryStatus msgStatus)
+ throws RemoteException {}
+
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
@@ -685,6 +714,76 @@ public abstract class IContextHubWrapper {
hub.registerEndpoint(info);
}
+ @Override
+ public int[] requestSessionIdRange(int size) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return null;
+ }
+ return hub.requestSessionIdRange(size);
+ }
+
+ @Override
+ public void openEndpointSession(
+ int sessionId,
+ EndpointId destination,
+ EndpointId initiator,
+ String serviceDescriptor)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.openEndpointSession(sessionId, destination, initiator, serviceDescriptor);
+ }
+
+ @Override
+ public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.closeEndpointSession(sessionId, reason);
+ }
+
+ @Override
+ public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.unregisterEndpoint(info);
+ }
+
+ @Override
+ public void endpointSessionOpenComplete(int sessionId) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.endpointSessionOpenComplete(sessionId);
+ }
+
+ @Override
+ public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.sendMessageToEndpoint(sessionId, msg);
+ }
+
+ @Override
+ public void sendMessageDeliveryStatusToEndpoint(
+ int sessionId, MessageDeliveryStatus msgStatus) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.sendMessageDeliveryStatusToEndpoint(sessionId, msgStatus);
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 0da1514872d6..bbd8aa1aac44 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -16,6 +16,9 @@
package com.android.server.location.fudger;
+import static com.android.internal.location.geometry.S2CellIdUtils.LAT_INDEX;
+import static com.android.internal.location.geometry.S2CellIdUtils.LNG_INDEX;
+
import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.location.Location;
@@ -184,31 +187,33 @@ public class LocationFudger {
synchronized (this) {
cacheCopy = mLocationFudgerCache;
}
-
+ double[] coarsened = new double[] {0.0, 0.0};
// TODO(b/381204398): To ensure a safe rollout, two algorithms co-exist. The first is the
// new density-based algorithm, while the second is the traditional coarsening algorithm.
// Once rollout is done, clean up the unused algorithm.
- if (Flags.densityBasedCoarseLocations() && cacheCopy != null
- && cacheCopy.hasDefaultValue()) {
- int level = cacheCopy.getCoarseningLevel(latitude, longitude);
- double[] center = snapToCenterOfS2Cell(latitude, longitude, level);
- latitude = center[S2CellIdUtils.LAT_INDEX];
- longitude = center[S2CellIdUtils.LNG_INDEX];
+ // The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been
+ // set, and (3) the cache has successfully queried the provider for the default coarsening
+ // value.
+ if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations()
+ && cacheCopy != null) {
+ if (cacheCopy.hasDefaultValue()) {
+ // New algorithm that snaps to the center of a S2 cell.
+ int level = cacheCopy.getCoarseningLevel(latitude, longitude);
+ coarsened = snapToCenterOfS2Cell(latitude, longitude, level);
+ } else {
+ // Try to fetch the default value. The answer won't come in time, but will be used
+ // for the next location to coarsen.
+ cacheCopy.fetchDefaultCoarseningLevelIfNeeded();
+ // Previous algorithm that snaps to a grid of width mAccuracyM.
+ coarsened = snapToGrid(latitude, longitude);
+ }
} else {
- // quantize location by snapping to a grid. this is the primary means of obfuscation. it
- // gives nice consistent results and is very effective at hiding the true location (as
- // long as you are not sitting on a grid boundary, which the random offsets mitigate).
- //
- // note that we quantize the latitude first, since the longitude quantization depends on
- // the latitude value and so leaks information about the latitude
- double latGranularity = metersToDegreesLatitude(mAccuracyM);
- latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
- double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
- longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+ // Previous algorithm that snaps to a grid of width mAccuracyM.
+ coarsened = snapToGrid(latitude, longitude);
}
- coarse.setLatitude(latitude);
- coarse.setLongitude(longitude);
+ coarse.setLatitude(coarsened[LAT_INDEX]);
+ coarse.setLongitude(coarsened[LNG_INDEX]);
coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));
synchronized (this) {
@@ -219,6 +224,21 @@ public class LocationFudger {
return coarse;
}
+ // quantize location by snapping to a grid. this is the primary means of obfuscation. it
+ // gives nice consistent results and is very effective at hiding the true location (as
+ // long as you are not sitting on a grid boundary, which the random offsets mitigate).
+ //
+ // note that we quantize the latitude first, since the longitude quantization depends on
+ // the latitude value and so leaks information about the latitude
+ private double[] snapToGrid(double latitude, double longitude) {
+ double[] center = new double[] {0.0, 0.0};
+ double latGranularity = metersToDegreesLatitude(mAccuracyM);
+ center[LAT_INDEX] = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
+ double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
+ center[LNG_INDEX] = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+ return center;
+ }
+
@VisibleForTesting
protected double[] snapToCenterOfS2Cell(double latDegrees, double lngDegrees, int level) {
long leafCell = S2CellIdUtils.fromLatLngDegrees(latDegrees, lngDegrees);
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
index ce8bec8f0147..19ec38ce8ccc 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
@@ -76,6 +76,13 @@ public class LocationFudgerCache {
asyncFetchDefaultCoarseningLevel();
}
+ /** If the cache's default coarsening value hasn't been set, asynchronously fetches it. */
+ public void fetchDefaultCoarseningLevelIfNeeded() {
+ if (!hasDefaultValue()) {
+ asyncFetchDefaultCoarseningLevel();
+ }
+ }
+
/** Returns true if the cache has successfully received a default value from the provider. */
public boolean hasDefaultValue() {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 0f65d1d44789..2686f2b30d27 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -46,6 +46,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.media.BluetoothRouteController.NoOpBluetoothRouteController;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -378,7 +379,12 @@ import java.util.Objects;
Slog.e(
TAG,
"Could not map this selected device attribute type to an available route: "
- + selectedDeviceAttributesType);
+ + selectedDeviceAttributesType
+ + ". Available types: "
+ + Arrays.toString(
+ Arrays.stream(audioDeviceInfos)
+ .map(AudioDeviceInfo::getType)
+ .toArray()));
// We know mRouteIdToAvailableDeviceRoutes is not empty.
newSelectedRouteHolder = mRouteIdToAvailableDeviceRoutes.values().iterator().next();
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index db1e6b465ff8..f09be2c15ee0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -66,6 +66,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private final int mUserId;
private final Handler mHandler;
private final boolean mIsSelfScanOnlyProvider;
+ private final boolean mSupportsSystemMediaRouting;
private final ServiceConnection mServiceConnection = new ServiceConnectionImpl();
// Connection state
@@ -95,12 +96,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
@NonNull Looper looper,
@NonNull ComponentName componentName,
boolean isSelfScanOnlyProvider,
+ boolean supportsSystemMediaRouting,
int userId) {
super(componentName, /* isSystemRouteProvider= */ false);
mContext = Objects.requireNonNull(context, "Context must not be null.");
mRequestIdToSessionCreationRequest = new LongSparseArray<>();
mSessionOriginalIdToTransferRequest = new HashMap<>();
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
+ mSupportsSystemMediaRouting = supportsSystemMediaRouting;
mUserId = userId;
mHandler = new Handler(looper);
}
@@ -276,13 +279,22 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
if (!mRunning) {
return false;
}
+ // We bind if any manager is scanning (regardless of whether an app is scanning) to give
+ // the opportunity for providers to publish routing sessions that were established
+ // directly between the app and the provider (typically via AndroidX MediaRouter). See
+ // b/176774510#comment20 for more information.
boolean bindDueToManagerScan =
mIsManagerScanning && !Flags.enablePreventionOfManagerScansWhenNoAppsScan();
- if (!getSessionInfos().isEmpty() || bindDueToManagerScan) {
- // We bind if any manager is scanning (regardless of whether an app is scanning) to give
- // the opportunity for providers to publish routing sessions that were established
- // directly between the app and the provider (typically via AndroidX MediaRouter). See
- // b/176774510#comment20 for more information.
+ // We also bind if this provider supports system media routing, because even if an app
+ // doesn't have any registered discovery preference, we should still be able to route their
+ // system media.
+ boolean bindDueToSystemMediaRoutingSupport =
+ mLastDiscoveryPreference != null
+ && mLastDiscoveryPreference.shouldPerformActiveScan()
+ && mSupportsSystemMediaRouting;
+ if (!getSessionInfos().isEmpty()
+ || bindDueToManagerScan
+ || bindDueToSystemMediaRoutingSupport) {
return true;
}
boolean anAppIsScanning =
@@ -651,11 +663,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
}
return TextUtils.formatSimple(
"ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b), "
- + "pending (session creations: %d, transfers: %d)",
+ + "system media=%b, pending (session creations: %d, transfers: %d)",
mComponentName.getPackageName(),
mBound,
mActiveConnection != null,
mConnectionReady,
+ mSupportsSystemMediaRouting,
pendingSessionCreationCount,
pendingTransferCount);
}
@@ -697,7 +710,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
Connection(IMediaRoute2ProviderService serviceBinder) {
mService = serviceBinder;
- mCallbackStub = new ServiceCallbackStub(this);
+ mCallbackStub = new ServiceCallbackStub(this, mSupportsSystemMediaRouting);
}
public boolean register() {
@@ -811,9 +824,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private static final class ServiceCallbackStub extends
IMediaRoute2ProviderServiceCallback.Stub {
private final WeakReference<Connection> mConnectionRef;
+ private final boolean mAllowSystemMediaRoutes;
- ServiceCallbackStub(Connection connection) {
+ ServiceCallbackStub(Connection connection, boolean allowSystemMediaRoutes) {
mConnectionRef = new WeakReference<>(connection);
+ mAllowSystemMediaRoutes = allowSystemMediaRoutes;
}
public void dispose() {
@@ -846,6 +861,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
+ "Disallowed route: "
+ route);
}
+
+ if (route.supportsSystemMediaRouting() && !mAllowSystemMediaRoutes) {
+ throw new SecurityException(
+ "This provider is not allowed to publish routes that support system"
+ + " media routing. Disallowed route: "
+ + route);
+ }
}
Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 93ef6f044e1b..42303e042561 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -17,7 +17,9 @@
package com.android.server.media;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import android.Manifest;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -30,6 +32,7 @@ import android.content.pm.ServiceInfo;
import android.media.MediaRoute2ProviderService;
import android.os.Handler;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -130,27 +133,44 @@ final class MediaRoute2ProviderWatcher {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
if (serviceInfo != null) {
boolean isSelfScanOnlyProvider = false;
+ boolean supportsSystemMediaRouting = false;
Iterator<String> categoriesIterator = resolveInfo.filter.categoriesIterator();
if (categoriesIterator != null) {
while (categoriesIterator.hasNext()) {
+ String category = categoriesIterator.next();
isSelfScanOnlyProvider |=
- MediaRoute2ProviderService.CATEGORY_SELF_SCAN_ONLY.equals(
- categoriesIterator.next());
+ MediaRoute2ProviderService.CATEGORY_SELF_SCAN_ONLY.equals(category);
+ supportsSystemMediaRouting |=
+ MediaRoute2ProviderService.SERVICE_INTERFACE_SYSTEM_MEDIA.equals(
+ category);
}
}
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
if (sourceIndex < 0) {
+ supportsSystemMediaRouting &= Flags.enableMirroringInMediaRouter2();
+ supportsSystemMediaRouting &=
+ mPackageManager.checkPermission(
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ serviceInfo.packageName)
+ == PERMISSION_GRANTED;
MediaRoute2ProviderServiceProxy proxy =
new MediaRoute2ProviderServiceProxy(
mContext,
mHandler.getLooper(),
new ComponentName(serviceInfo.packageName, serviceInfo.name),
isSelfScanOnlyProvider,
+ supportsSystemMediaRouting,
mUserId);
Slog.i(
TAG,
- "Enabling proxy for MediaRoute2ProviderService: "
- + proxy.mComponentName);
+ TextUtils.formatSimple(
+ "Enabling proxy for MediaRoute2ProviderService: %s"
+ + " (isSelfScan=%b, supportsSystemMediaRouting=%b,"
+ + " userId=%d)",
+ proxy.mComponentName,
+ isSelfScanOnlyProvider,
+ supportsSystemMediaRouting,
+ mUserId));
proxy.start(/* rebindIfDisconnected= */ false);
mProxies.add(targetIndex++, proxy);
mCallback.onAddProviderService(proxy);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index ab68ed3e73c6..58deffcbd4ba 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -848,7 +848,7 @@ class MediaRouter2ServiceImpl {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
List<RoutingSessionInfo> sessionInfos;
if (hasSystemRoutingPermissions) {
- if (setDeviceRouteSelected) {
+ if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
return userRecord.mHandler.getSystemProvider()
@@ -2347,13 +2347,24 @@ class MediaRouter2ServiceImpl {
if (!Flags.enableRouteVisibilityControlApi()) {
return true;
}
- for (String permission : route.getRequiredPermissions()) {
- if (mContext.checkPermission(permission, mPid, mUid)
- != PackageManager.PERMISSION_GRANTED) {
- return false;
+ List<Set<String>> permissionSets = route.getRequiredPermissions();
+ if (permissionSets.isEmpty()) {
+ return true;
+ }
+ for (Set<String> permissionSet : permissionSets) {
+ boolean hasAllInSet = true;
+ for (String permission : permissionSet) {
+ if (mContext.checkPermission(permission, mPid, mUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ hasAllInSet = false;
+ break;
+ }
+ }
+ if (hasAllInSet) {
+ return true;
}
}
- return true;
+ return false;
}
}
@@ -2570,9 +2581,9 @@ class MediaRouter2ServiceImpl {
mUserRecord = userRecord;
mSystemProvider =
Flags.enableMirroringInMediaRouter2()
- ? new SystemMediaRoute2Provider2(
+ ? SystemMediaRoute2Provider2.create(
service.mContext, UserHandle.of(userRecord.mUserId), looper)
- : new SystemMediaRoute2Provider(
+ : SystemMediaRoute2Provider.create(
service.mContext, UserHandle.of(userRecord.mUserId), looper);
mRouteProviders.add(getSystemProvider());
mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
@@ -2722,6 +2733,15 @@ class MediaRouter2ServiceImpl {
newRoutes = Collections.emptySet();
}
+ if (Flags.enableMirroringInMediaRouter2()
+ && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) {
+ // We notify the system provider of service updates, so that it can update the
+ // system routing session by adding them as transferable routes. And we remove those
+ // that don't support remote routing.
+ mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider);
+ newRoutes.removeIf(it -> !it.supportsRemoteRouting());
+ }
+
// Add new routes to the maps.
ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>();
boolean hasAddedOrModifiedRoutes = false;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5fb80ba92091..b93846bf9ee7 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -73,6 +73,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
// For apps without MODIFYING_AUDIO_ROUTING permission.
// This should be the currently selected route.
MediaRoute2Info mDefaultRoute;
+
+ @GuardedBy("mLock")
+ RoutingSessionInfo mSystemSessionInfo;
+
RoutingSessionInfo mDefaultSessionInfo;
private final AudioManagerBroadcastReceiver mAudioReceiver =
@@ -89,8 +93,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@Nullable
private volatile SessionCreationOrTransferRequest mPendingTransferRequest;
- SystemMediaRoute2Provider(Context context, UserHandle user, Looper looper) {
- this(context, COMPONENT_NAME, user, looper);
+ public static SystemMediaRoute2Provider create(
+ Context context, UserHandle user, Looper looper) {
+ var instance = new SystemMediaRoute2Provider(context, COMPONENT_NAME, user, looper);
+ instance.updateProviderState();
+ instance.updateSessionInfosIfNeeded();
+ return instance;
}
protected SystemMediaRoute2Provider(
@@ -124,8 +132,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
notifySessionInfoUpdated();
}
}));
- updateProviderState();
- updateSessionInfosIfNeeded();
}
public void start() {
@@ -178,7 +184,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
RoutingSessionInfo currentSessionInfo;
synchronized (mLock) {
- currentSessionInfo = mSessionInfos.get(0);
+ currentSessionInfo =
+ Flags.enableMirroringInMediaRouter2()
+ ? mSystemSessionInfo
+ : mSessionInfos.get(0);
}
mCallback.onSessionCreated(this, requestId, currentSessionInfo);
return;
@@ -352,7 +361,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
- RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0);
+ var oldSessionInfo =
+ Flags.enableMirroringInMediaRouter2()
+ ? mSystemSessionInfo
+ : mSessionInfos.get(0);
builder.setTransferReason(oldSessionInfo.getTransferReason())
.setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(),
oldSessionInfo.getTransferInitiatorPackageName());
@@ -362,7 +374,32 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
}
- private void updateProviderState() {
+ /**
+ * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update.
+ *
+ * <p>To be overridden so as to generate system media routes for {@link
+ * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting()
+ * support system media routing}).
+ *
+ * @param serviceProxy The proxy of the service that updated its state.
+ */
+ public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
+ // Do nothing. This implementation doesn't support MR2ProviderService system media routes.
+ // The subclass overrides this method to implement app-managed system media routing (aka
+ // mirroring).
+ }
+
+ /**
+ * Called when the system provider state changes.
+ *
+ * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media
+ * routing routes are added before setting the provider state.
+ */
+ public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
+ setProviderState(providerInfo);
+ }
+
+ protected void updateProviderState() {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
// We must have a device route in the provider info.
@@ -371,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
for (MediaRoute2Info route : deviceRoutes) {
builder.addRoute(route);
}
- setProviderState(builder.build());
+ if (!Flags.enableMirroringInMediaRouter2()) {
+ setProviderState(builder.build());
+ }
} else {
builder.addRoute(mDeviceRouteController.getSelectedRoute());
}
@@ -380,7 +419,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
builder.addRoute(route);
}
MediaRoute2ProviderInfo providerInfo = builder.build();
- setProviderState(providerInfo);
+ onSystemProviderRoutesChanged(providerInfo);
if (DEBUG) {
Slog.d(TAG, "Updating system provider info : " + providerInfo);
}
@@ -391,8 +430,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
*/
boolean updateSessionInfosIfNeeded() {
synchronized (mLock) {
- RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
- 0);
+ RoutingSessionInfo oldSessionInfo;
+ if (Flags.enableMirroringInMediaRouter2()) {
+ oldSessionInfo = mSystemSessionInfo;
+ } else {
+ oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
+ }
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -481,8 +524,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (DEBUG) {
Slog.d(TAG, "Updating system routing session info : " + newSessionInfo);
}
- mSessionInfos.clear();
- mSessionInfos.add(newSessionInfo);
+ mSystemSessionInfo = newSessionInfo;
+ onSystemSessionInfoUpdated();
mDefaultSessionInfo =
new RoutingSessionInfo.Builder(
SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -499,6 +542,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
}
+ @GuardedBy("mLock")
+ protected void onSystemSessionInfoUpdated() {
+ mSessionInfos.clear();
+ mSessionInfos.add(mSystemSessionInfo);
+ }
+
@GuardedBy("mRequestLock")
private void reportPendingSessionRequestResultLockedIfNeeded(
RoutingSessionInfo newSessionInfo) {
@@ -585,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
RoutingSessionInfo sessionInfo;
synchronized (mLock) {
sessionInfo = mSessionInfos.get(0);
+ if (sessionInfo == null) {
+ return;
+ }
}
mCallback.onSessionUpdated(this, sessionInfo);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 8a14a3866860..7dc30ab66fd2 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -16,11 +16,26 @@
package com.android.server.media;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.RoutingSessionInfo;
import android.os.Looper;
import android.os.UserHandle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
/**
* Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link
@@ -30,12 +45,142 @@ import android.os.UserHandle;
*/
/* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider {
+ private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM";
+ private static final String ROUTE_ID_SYSTEM_SEPARATOR = ".";
+
+ @GuardedBy("mLock")
+ private MediaRoute2ProviderInfo mLastSystemProviderInfo;
+
+ @GuardedBy("mLock")
+ private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>();
+
private static final ComponentName COMPONENT_NAME =
new ComponentName(
SystemMediaRoute2Provider2.class.getPackage().getName(),
SystemMediaRoute2Provider2.class.getName());
- SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
+ public static SystemMediaRoute2Provider2 create(
+ Context context, UserHandle user, Looper looper) {
+ var instance = new SystemMediaRoute2Provider2(context, user, looper);
+ instance.updateProviderState();
+ instance.updateSessionInfosIfNeeded();
+ return instance;
+ }
+
+ private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
super(context, COMPONENT_NAME, user, looper);
}
+
+ @Override
+ protected void onSystemSessionInfoUpdated() {
+ updateSessionInfo();
+ }
+
+ @Override
+ public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
+ var proxyRecord = ProviderProxyRecord.createFor(serviceProxy);
+ synchronized (mLock) {
+ if (proxyRecord == null) {
+ mProxyRecords.remove(serviceProxy.mUniqueId);
+ } else {
+ mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord);
+ }
+ setProviderState(buildProviderInfo());
+ }
+ updateSessionInfo();
+ notifyProviderState();
+ notifySessionInfoUpdated();
+ }
+
+ @Override
+ public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
+ synchronized (mLock) {
+ mLastSystemProviderInfo = providerInfo;
+ setProviderState(buildProviderInfo());
+ }
+ updateSessionInfo();
+ notifySessionInfoUpdated();
+ }
+
+ /**
+ * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session
+ * with information from the {@link MediaRoute2ProviderService provider services}.
+ */
+ private void updateSessionInfo() {
+ synchronized (mLock) {
+ var systemSessionInfo = mSystemSessionInfo;
+ if (systemSessionInfo == null) {
+ // The system session info hasn't been initialized yet. Do nothing.
+ return;
+ }
+ var builder = new RoutingSessionInfo.Builder(systemSessionInfo);
+ mProxyRecords.values().stream()
+ .flatMap(ProviderProxyRecord::getRoutesStream)
+ .map(MediaRoute2Info::getId)
+ .forEach(builder::addTransferableRoute);
+ mSessionInfos.clear();
+ mSessionInfos.add(builder.build());
+ }
+ }
+
+ /**
+ * Returns a new a provider info that includes all routes from the system provider {@link
+ * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService
+ * provider services}.
+ */
+ @GuardedBy("mLock")
+ private MediaRoute2ProviderInfo buildProviderInfo() {
+ MediaRoute2ProviderInfo.Builder builder =
+ new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo);
+ mProxyRecords.values().stream()
+ .flatMap(ProviderProxyRecord::getRoutesStream)
+ .forEach(builder::addRoute);
+ return builder.build();
+ }
+
+ /**
+ * Holds information about {@link MediaRoute2ProviderService provider services} registered in
+ * the system.
+ *
+ * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}.
+ * @param mSystemMediaRoutes The last snapshot of routes from the service that support system
+ * media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}.
+ */
+ private record ProviderProxyRecord(
+ MediaRoute2ProviderServiceProxy mProxy,
+ Collection<MediaRoute2Info> mSystemMediaRoutes) {
+
+ /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */
+ public Stream<MediaRoute2Info> getRoutesStream() {
+ return mSystemMediaRoutes.stream();
+ }
+
+ /**
+ * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an
+ * associated {@link MediaRoute2ProviderInfo}.
+ */
+ @Nullable
+ public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) {
+ MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo();
+ if (providerInfo == null) {
+ return null;
+ }
+ ArraySet<MediaRoute2Info> routes = new ArraySet<>();
+ providerInfo.getRoutes().stream()
+ .filter(MediaRoute2Info::supportsSystemMediaRouting)
+ .forEach(
+ route -> {
+ String id =
+ ROUTE_ID_PREFIX_SYSTEM
+ + route.getProviderId()
+ + ROUTE_ID_SYSTEM_SEPARATOR
+ + route.getOriginalId();
+ routes.add(
+ new MediaRoute2Info.Builder(id, route.getName())
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .build());
+ });
+ return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/quality/BiMap.java b/services/core/java/com/android/server/media/quality/BiMap.java
new file mode 100644
index 000000000000..82b82847a29f
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/BiMap.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.media.quality;
+
+import android.util.ArrayMap;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A very basic bidirectional map.
+ *
+ * @param <K> data type of Key
+ * @param <V> data type of Value
+ */
+public class BiMap<K, V> {
+ private Map<K, V> mPrimaryMap = new ArrayMap<>();
+ private Map<V, K> mSecondaryMap = new ArrayMap<>();
+
+ /**
+ * Add key and associated value to the map
+ *
+ * @param key key to add
+ * @param value value to add
+ * @return true if successfully added, false otherwise
+ */
+ public boolean put(K key, V value) {
+ if (key == null || value == null || mPrimaryMap.containsKey(key)
+ || mSecondaryMap.containsKey(value)) {
+ return false;
+ }
+
+ mPrimaryMap.put(key, value);
+ mSecondaryMap.put(value, key);
+ return true;
+ }
+
+ /**
+ * Remove key and associated value from the map
+ *
+ * @param key key to remove
+ * @return true if removed, false otherwise
+ */
+ public boolean remove(K key) {
+ if (key == null) {
+ return false;
+ }
+ if (mPrimaryMap.containsKey(key)) {
+ V value = getValue(key);
+ mPrimaryMap.remove(key);
+ mSecondaryMap.remove(value);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove value and associated key from the map
+ *
+ * @param value value to remove
+ * @return true if removed, false otherwise
+ */
+ public boolean removeValue(V value) {
+ if (value == null) {
+ return false;
+ }
+ return remove(getKey(value));
+ }
+
+ /**
+ * Get the value
+ *
+ * @param key key for which to get value
+ * @return V
+ */
+ public V getValue(K key) {
+ return mPrimaryMap.get(key);
+ }
+
+ /**
+ * Get the key
+ *
+ * @param value value for which to get key
+ * @return K
+ */
+ public K getKey(V value) {
+ return mSecondaryMap.get(value);
+ }
+
+ /**
+ * Get the values of the map.
+ * @return Collection
+ */
+ public Collection<V> getValues() {
+ return mPrimaryMap.values();
+ }
+
+ /**
+ * Clear the map
+ */
+ public void clear() {
+ mPrimaryMap.clear();
+ mSecondaryMap.clear();
+ }
+}
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 4479e9dceb17..c81023144c0f 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -30,23 +30,24 @@ import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
import android.media.quality.PictureProfileHandle;
import android.media.quality.SoundProfile;
+import android.media.quality.SoundProfileHandle;
+import android.os.Binder;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.util.Log;
import com.android.server.SystemService;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.stream.Collectors;
import java.util.UUID;
+import java.util.stream.Collectors;
/**
* This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -56,6 +57,7 @@ public class MediaQualityService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "MediaQualityService";
+ private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
private final Context mContext;
private final MediaQualityDbHelper mMediaQualityDbHelper;
private final BiMap<Long, String> mPictureProfileTempIdMap;
@@ -64,8 +66,8 @@ public class MediaQualityService extends SystemService {
public MediaQualityService(Context context) {
super(context);
mContext = context;
- mPictureProfileTempIdMap = HashBiMap.create();
- mSoundProfileTempIdMap = HashBiMap.create();
+ mPictureProfileTempIdMap = new BiMap<>();
+ mSoundProfileTempIdMap = new BiMap<>();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
mMediaQualityDbHelper.setIdleConnectionTimeout(30);
@@ -80,32 +82,43 @@ public class MediaQualityService extends SystemService {
private final class BinderService extends IMediaQualityManager.Stub {
@Override
- public PictureProfile createPictureProfile(PictureProfile pp, int userId) {
+ public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(BaseParameters.PARAMETER_TYPE, pp.getProfileType());
- values.put(BaseParameters.PARAMETER_NAME, pp.getName());
- values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
- values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
- values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(pp.getParameters()));
+ ContentValues values = getContentValues(null,
+ pp.getProfileType(),
+ pp.getName(),
+ pp.getPackageName(),
+ pp.getInputId(),
+ pp.getParameters());
// id is auto-generated by SQLite upon successful insertion of row
Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
null, values);
populateTempIdMap(mPictureProfileTempIdMap, id);
- pp.setProfileId(mPictureProfileTempIdMap.get(id));
+ pp.setProfileId(mPictureProfileTempIdMap.getValue(id));
return pp;
}
@Override
- public void updatePictureProfile(String id, PictureProfile pp, int userId) {
- // TODO: implement
+ public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
+ Long intId = mPictureProfileTempIdMap.getKey(id);
+
+ ContentValues values = getContentValues(intId,
+ pp.getProfileType(),
+ pp.getName(),
+ pp.getPackageName(),
+ pp.getInputId(),
+ pp.getParameters());
+
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ db.replace(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+ null, values);
}
@Override
- public void removePictureProfile(String id, int userId) {
- Long intId = mPictureProfileTempIdMap.inverse().get(id);
+ public void removePictureProfile(String id, UserHandle user) {
+ Long intId = mPictureProfileTempIdMap.getKey(id);
if (intId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
@@ -117,7 +130,8 @@ public class MediaQualityService extends SystemService {
}
@Override
- public PictureProfile getPictureProfile(int type, String name, int userId) {
+ public PictureProfile getPictureProfile(int type, String name, boolean includeParams,
+ UserHandle user) {
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ BaseParameters.PARAMETER_NAME + " = ?";
String[] selectionArguments = {Integer.toString(type), name};
@@ -125,7 +139,7 @@ public class MediaQualityService extends SystemService {
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- getAllMediaProfileColumns(), selection, selectionArguments)
+ getMediaProfileColumns(includeParams), selection, selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -143,26 +157,33 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<PictureProfile> getPictureProfilesByPackage(String packageName, int userId) {
+ public List<PictureProfile> getPictureProfilesByPackage(
+ String packageName, boolean includeParams, UserHandle user) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
- selectionArguments);
+ return getPictureProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+ selection, selectionArguments);
}
@Override
- public List<PictureProfile> getAvailablePictureProfiles(int userId) {
+ public List<PictureProfile> getAvailablePictureProfiles(
+ boolean includeParams, UserHandle user) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+ return getPictureProfilesByPackage(packageNames[0], includeParams, user);
+ }
return new ArrayList<>();
}
@Override
- public boolean setDefaultPictureProfile(String profileId, int userId) {
+ public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
- public List<String> getPictureProfilePackageNames(int userId) {
+ public List<String> getPictureProfilePackageNames(UserHandle user) {
String [] column = {BaseParameters.PARAMETER_PACKAGE};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
@@ -173,37 +194,52 @@ public class MediaQualityService extends SystemService {
}
@Override
- public PictureProfileHandle getPictureProfileHandle(String id, int userId) {
- return null;
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] id, UserHandle user) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] id, UserHandle user) {
+ return new ArrayList<>();
}
@Override
- public SoundProfile createSoundProfile(SoundProfile sp, int userId) {
+ public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(BaseParameters.PARAMETER_TYPE, sp.getProfileType());
- values.put(BaseParameters.PARAMETER_NAME, sp.getName());
- values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
- values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
- values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(sp.getParameters()));
+ ContentValues values = getContentValues(null,
+ sp.getProfileType(),
+ sp.getName(),
+ sp.getPackageName(),
+ sp.getInputId(),
+ sp.getParameters());
// id is auto-generated by SQLite upon successful insertion of row
Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
null, values);
populateTempIdMap(mSoundProfileTempIdMap, id);
- sp.setProfileId(mSoundProfileTempIdMap.get(id));
+ sp.setProfileId(mSoundProfileTempIdMap.getValue(id));
return sp;
}
@Override
- public void updateSoundProfile(String id, SoundProfile pp, int userId) {
- // TODO: implement
+ public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
+ Long intId = mSoundProfileTempIdMap.getKey(id);
+
+ ContentValues values = getContentValues(intId,
+ sp.getProfileType(),
+ sp.getName(),
+ sp.getPackageName(),
+ sp.getInputId(),
+ sp.getParameters());
+
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
}
@Override
- public void removeSoundProfile(String id, int userId) {
- Long intId = mSoundProfileTempIdMap.inverse().get(id);
+ public void removeSoundProfile(String id, UserHandle user) {
+ Long intId = mSoundProfileTempIdMap.getKey(id);
if (intId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
@@ -215,15 +251,16 @@ public class MediaQualityService extends SystemService {
}
@Override
- public SoundProfile getSoundProfile(int type, String id, int userId) {
+ public SoundProfile getSoundProfile(int type, String id, boolean includeParams,
+ UserHandle user) {
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_NAME + " = ?";
+ + BaseParameters.PARAMETER_ID + " = ?";
String[] selectionArguments = {String.valueOf(type), id};
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
- getAllMediaProfileColumns(), selection, selectionArguments)
+ getMediaProfileColumns(includeParams), selection, selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -241,26 +278,33 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<SoundProfile> getSoundProfilesByPackage(String packageName, int userId) {
+ public List<SoundProfile> getSoundProfilesByPackage(
+ String packageName, boolean includeParams, UserHandle user) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
- selectionArguments);
+ return getSoundProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+ selection, selectionArguments);
}
@Override
- public List<SoundProfile> getAvailableSoundProfiles(int userId) {
+ public List<SoundProfile> getAvailableSoundProfiles(
+ boolean includeParams, UserHandle user) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+ return getSoundProfilesByPackage(packageNames[0], includeParams, user);
+ }
return new ArrayList<>();
}
@Override
- public boolean setDefaultSoundProfile(String profileId, int userId) {
+ public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
- public List<String> getSoundProfilePackageNames(int userId) {
+ public List<String> getSoundProfilePackageNames(UserHandle user) {
String [] column = {BaseParameters.PARAMETER_NAME};
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
null, null);
@@ -271,12 +315,17 @@ public class MediaQualityService extends SystemService {
}
private void populateTempIdMap(BiMap<Long, String> map, Long id) {
- if (id != null && map.get(id) == null) {
- String uuid = UUID.randomUUID().toString();
- while (map.inverse().containsKey(uuid)) {
+ if (id != null && map.getValue(id) == null) {
+ String uuid;
+ int attempts = 0;
+ while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
uuid = UUID.randomUUID().toString();
+ if (map.getKey(uuid) == null) {
+ map.put(id, uuid);
+ return;
+ }
+ attempts++;
}
- map.put(id, uuid);
}
}
@@ -303,7 +352,7 @@ public class MediaQualityService extends SystemService {
return json.toString();
}
- private PersistableBundle jsonToBundle(String jsonString) {
+ private PersistableBundle jsonToPersistableBundle(String jsonString) {
PersistableBundle bundle = new PersistableBundle();
if (jsonString != null) {
JSONObject jsonObject = null;
@@ -334,15 +383,42 @@ public class MediaQualityService extends SystemService {
return bundle;
}
- private String[] getAllMediaProfileColumns() {
- return new String[]{
+ private ContentValues getContentValues(Long dbId, Integer profileType, String name,
+ String packageName, String inputId, PersistableBundle params) {
+ ContentValues values = new ContentValues();
+ if (dbId != null) {
+ values.put(BaseParameters.PARAMETER_ID, dbId);
+ }
+ if (profileType != null) {
+ values.put(BaseParameters.PARAMETER_TYPE, profileType);
+ }
+ if (name != null) {
+ values.put(BaseParameters.PARAMETER_NAME, name);
+ }
+ if (packageName != null) {
+ values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
+ }
+ if (inputId != null) {
+ values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
+ }
+ if (params != null) {
+ values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(params));
+ }
+ return values;
+ }
+
+ private String[] getMediaProfileColumns(boolean includeParams) {
+ ArrayList<String> columns = new ArrayList<>(Arrays.asList(
BaseParameters.PARAMETER_ID,
BaseParameters.PARAMETER_TYPE,
BaseParameters.PARAMETER_NAME,
BaseParameters.PARAMETER_INPUT_ID,
- BaseParameters.PARAMETER_PACKAGE,
- mMediaQualityDbHelper.SETTINGS
- };
+ BaseParameters.PARAMETER_PACKAGE)
+ );
+ if (includeParams) {
+ columns.add(mMediaQualityDbHelper.SETTINGS);
+ }
+ return columns.toArray(new String[0]);
}
private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
@@ -352,7 +428,8 @@ public class MediaQualityService extends SystemService {
getName(cursor),
getInputId(cursor),
getPackageName(cursor),
- jsonToBundle(getSettingsString(cursor))
+ jsonToPersistableBundle(getSettingsString(cursor)),
+ PictureProfileHandle.NONE
);
}
@@ -363,7 +440,8 @@ public class MediaQualityService extends SystemService {
getName(cursor),
getInputId(cursor),
getPackageName(cursor),
- jsonToBundle(getSettingsString(cursor))
+ jsonToPersistableBundle(getSettingsString(cursor)),
+ SoundProfileHandle.NONE
);
}
@@ -371,7 +449,7 @@ public class MediaQualityService extends SystemService {
int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
populateTempIdMap(map, dbId);
- return map.get(dbId);
+ return map.getValue(dbId);
}
private int getType(Cursor cursor) {
@@ -448,70 +526,71 @@ public class MediaQualityService extends SystemService {
}
@Override
- public void setAmbientBacklightSettings(AmbientBacklightSettings settings, int userId) {
+ public void setAmbientBacklightSettings(
+ AmbientBacklightSettings settings, UserHandle user) {
}
@Override
- public void setAmbientBacklightEnabled(boolean enabled, int userId) {
+ public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
}
@Override
- public List<ParamCapability> getParamCapabilities(List<String> names, int userId) {
+ public List<ParamCapability> getParamCapabilities(List<String> names, UserHandle user) {
return new ArrayList<>();
}
@Override
- public List<String> getPictureProfileAllowList(int userId) {
+ public List<String> getPictureProfileAllowList(UserHandle user) {
return new ArrayList<>();
}
@Override
- public void setPictureProfileAllowList(List<String> packages, int userId) {
+ public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
}
@Override
- public List<String> getSoundProfileAllowList(int userId) {
+ public List<String> getSoundProfileAllowList(UserHandle user) {
return new ArrayList<>();
}
@Override
- public void setSoundProfileAllowList(List<String> packages, int userId) {
+ public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
}
@Override
- public boolean isSupported(int userId) {
+ public boolean isSupported(UserHandle user) {
return false;
}
@Override
- public void setAutoPictureQualityEnabled(boolean enabled, int userId) {
+ public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isAutoPictureQualityEnabled(int userId) {
+ public boolean isAutoPictureQualityEnabled(UserHandle user) {
return false;
}
@Override
- public void setSuperResolutionEnabled(boolean enabled, int userId) {
+ public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isSuperResolutionEnabled(int userId) {
+ public boolean isSuperResolutionEnabled(UserHandle user) {
return false;
}
@Override
- public void setAutoSoundQualityEnabled(boolean enabled, int userId) {
+ public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isAutoSoundQualityEnabled(int userId) {
+ public boolean isAutoSoundQualityEnabled(UserHandle user) {
return false;
}
@Override
- public boolean isAmbientBacklightEnabled(int userId) {
+ public boolean isAmbientBacklightEnabled(UserHandle user) {
return false;
}
}
diff --git a/services/core/java/com/android/server/media/quality/OWNERS b/services/core/java/com/android/server/media/quality/OWNERS
index e455846dd75d..7171aa4ce9b6 100644
--- a/services/core/java/com/android/server/media/quality/OWNERS
+++ b/services/core/java/com/android/server/media/quality/OWNERS
@@ -1,2 +1,3 @@
shubang@google.com
-haofanw@google.com \ No newline at end of file
+haofanw@google.com
+pkandhalu@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 15af36ba66af..c6d7fc7508da 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -361,6 +361,7 @@ import com.android.server.lights.LightsManager;
import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
import com.android.server.notification.toast.CustomToastRecord;
import com.android.server.notification.toast.TextToastRecord;
import com.android.server.notification.toast.ToastRecord;
@@ -1934,10 +1935,20 @@ public class NotificationManagerService extends SystemService {
hasSensitiveContent, lifespanMs);
}
- protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
- int classification, int lifespanMs) {
+ protected void logClassificationChannelAdjustmentReceived(NotificationRecord r,
+ boolean hasPosted,
+ int classification) {
+ // Note that this value of isAlerting does not fully indicate whether a notif
+ // would make a sound or HUN on device; it is an approximation for metrics.
+ boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+ int instanceId = r.getSbn().getInstanceId() == null
+ ? 0 : r.getSbn().getInstanceId().getId();
+
FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_CLASSIFICATION,
- hasPosted, isAlerting, classification, lifespanMs);
+ hasPosted, isAlerting, classification,
+ r.getLifespanMs(System.currentTimeMillis()),
+ NotificationReportedEvent.NOTIFICATION_ADJUSTED.getId(),
+ instanceId, r.getUid());
}
protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
@@ -2594,6 +2605,7 @@ public class NotificationManagerService extends SystemService {
intent.setPackage(pkg);
intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, id);
intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS, status);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
getContext().sendBroadcastAsUser(intent, UserHandle.of(userId));
});
}
@@ -6133,7 +6145,7 @@ public class NotificationManagerService extends SystemService {
}
private void enforcePolicyAccess(int uid, String method) {
- if (PERMISSION_GRANTED == getContext().checkCallingPermission(
+ if (PERMISSION_GRANTED == getContext().checkCallingOrSelfPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
return;
}
@@ -6164,7 +6176,7 @@ public class NotificationManagerService extends SystemService {
}
private void enforcePolicyAccess(String pkg, String method) {
- if (PERMISSION_GRANTED == getContext().checkCallingPermission(
+ if (PERMISSION_GRANTED == getContext().checkCallingOrSelfPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
return;
}
@@ -6973,6 +6985,7 @@ public class NotificationManagerService extends SystemService {
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
+ // Safe to check calling permission as caller is already not system or phone
getContext().enforceCallingPermission(
permission.MANAGE_NOTIFICATION_LISTENERS,
"Caller must hold " + permission.MANAGE_NOTIFICATION_LISTENERS);
@@ -7051,11 +7064,8 @@ public class NotificationManagerService extends SystemService {
int classification = adjustments.getInt(KEY_TYPE);
// swap app provided type with the real thing
adjustments.putParcelable(KEY_TYPE, newChannel);
- // Note that this value of isAlerting does not fully indicate whether a notif
- // would make a sound or HUN on device; it is an approximation for metrics.
- boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
- logClassificationChannelAdjustmentReceived(isPosted, isAlerting, classification,
- r.getLifespanMs(System.currentTimeMillis()));
+
+ logClassificationChannelAdjustmentReceived(r, isPosted, classification);
}
}
r.addAdjustment(adjustment);
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index c09077e349fd..f17ac5c92889 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.service.notification.RateEstimator;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index dc173b124884..b571d62c0cba 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -112,9 +112,10 @@ import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.FrameworkStatsLog;
@@ -279,6 +280,11 @@ public class ZenModeHelper {
mCallbacks.remove(callback);
}
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public List<Callback> getCallbacks() {
+ return mCallbacks;
+ }
+
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
synchronized (mConfigLock) {
@@ -1548,15 +1554,18 @@ public class ZenModeHelper {
if (isFromApp) {
// Don't allow apps to toggle hidden (non-public-API) effects.
- newEffects = new ZenDeviceEffects.Builder(newEffects)
- .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
- .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
- .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
- .setShouldDisableTouch(oldEffects.shouldDisableTouch())
- .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
- .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
- .setExtraEffects(oldEffects.getExtraEffects())
- .build();
+ newEffects =
+ new ZenDeviceEffects.Builder(newEffects)
+ .setShouldDisableAutoBrightness(
+ oldEffects.shouldDisableAutoBrightness())
+ .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+ .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+ .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+ .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+ .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+ .setShouldUseNightLight(oldEffects.shouldUseNightLight())
+ .setExtraEffects(oldEffects.getExtraEffects())
+ .build();
}
zenRule.zenDeviceEffects = newEffects;
@@ -1595,6 +1604,9 @@ public class ZenModeHelper {
if (oldEffects.shouldMaximizeDoze() != newEffects.shouldMaximizeDoze()) {
userModifiedFields |= ZenDeviceEffects.FIELD_MAXIMIZE_DOZE;
}
+ if (oldEffects.shouldUseNightLight() != newEffects.shouldUseNightLight()) {
+ userModifiedFields |= ZenDeviceEffects.FIELD_NIGHT_LIGHT;
+ }
if (!Objects.equals(oldEffects.getExtraEffects(), newEffects.getExtraEffects())) {
userModifiedFields |= ZenDeviceEffects.FIELD_EXTRA_EFFECTS;
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index f79d9ef174ea..65a38ae1fcde 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -123,13 +123,6 @@ flag {
}
flag {
- name: "use_ipcdatacache_channels"
- namespace: "systemui"
- description: "Adds an IPCDataCache for notification channel/group lookups"
- bug: "331677193"
-}
-
-flag {
name: "use_ssm_user_switch_signal"
namespace: "systemui"
description: "This flag controls which signal is used to handle a user switch system event"
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OWNERS b/services/core/java/com/android/server/ondeviceintelligence/OWNERS
deleted file mode 100644
index 09774f78d712..000000000000
--- a/services/core/java/com/android/server/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
index 8ec716077f46..871d12ee6394 100644
--- a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
+++ b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
@@ -20,116 +20,100 @@ import static android.Manifest.permission.DYNAMIC_INSTRUMENTATION;
import static android.content.Context.DYNAMIC_INSTRUMENTATION_SERVICE;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.PermissionManuallyEnforced;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManagerInternal;
import android.content.Context;
+import android.os.RemoteException;
import android.os.instrumentation.ExecutableMethodFileOffsets;
import android.os.instrumentation.IDynamicInstrumentationManager;
+import android.os.instrumentation.IOffsetCallback;
import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.os.instrumentation.TargetProcess;
-import com.android.internal.annotations.VisibleForTesting;
+
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import dalvik.system.VMDebug;
import java.lang.reflect.Method;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
/**
* System private implementation of the {@link IDynamicInstrumentationManager interface}.
*/
public class DynamicInstrumentationManagerService extends SystemService {
+
+ private ActivityManagerInternal mAmInternal;
+
public DynamicInstrumentationManagerService(@NonNull Context context) {
super(context);
}
@Override
public void onStart() {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
publishBinderService(DYNAMIC_INSTRUMENTATION_SERVICE, new BinderService());
}
private final class BinderService extends IDynamicInstrumentationManager.Stub {
@Override
@PermissionManuallyEnforced
- public @Nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
- @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor) {
+ @RequiresPermission(value = android.Manifest.permission.DYNAMIC_INSTRUMENTATION)
+ public void getExecutableMethodFileOffsets(
+ @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback) {
if (!com.android.art.flags.Flags.executableMethodFileOffsets()) {
throw new UnsupportedOperationException();
}
getContext().enforceCallingOrSelfPermission(
DYNAMIC_INSTRUMENTATION, "Caller must have DYNAMIC_INSTRUMENTATION permission");
+ Objects.requireNonNull(targetProcess.processName);
- if (targetProcess.processName == null
- || !targetProcess.processName.equals("system_server")) {
- throw new UnsupportedOperationException(
- "system_server is the only supported target process");
+ if (!targetProcess.processName.equals("system_server")) {
+ try {
+ mAmInternal.getExecutableMethodFileOffsets(targetProcess.processName,
+ targetProcess.pid, targetProcess.uid, methodDescriptor,
+ new IOffsetCallback.Stub() {
+ @Override
+ public void onResult(ExecutableMethodFileOffsets result) {
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ });
+ return;
+ } catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(
+ "The specified app process cannot be found." , e);
+ }
}
- Method method = parseMethodDescriptor(
+ Method method = MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(), methodDescriptor);
VMDebug.ExecutableMethodFileOffsets location =
VMDebug.getExecutableMethodFileOffsets(method);
- if (location == null) {
- return null;
- }
-
- ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
- ret.containerPath = location.getContainerPath();
- ret.containerOffset = location.getContainerOffset();
- ret.methodOffset = location.getMethodOffset();
- return ret;
- }
- }
-
- @VisibleForTesting
- static Method parseMethodDescriptor(ClassLoader classLoader,
- @NonNull MethodDescriptor descriptor) {
- try {
- Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
- Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length];
- for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) {
- String typeName = descriptor.fullyQualifiedParameters[i];
- boolean isArrayType = typeName.endsWith("[]");
- if (isArrayType) {
- typeName = typeName.substring(0, typeName.length() - 2);
+ try {
+ if (location == null) {
+ callback.onResult(null);
+ return;
}
- switch (typeName) {
- case "boolean":
- parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class;
- break;
- case "byte":
- parameters[i] = isArrayType ? byte.class.arrayType() : byte.class;
- break;
- case "char":
- parameters[i] = isArrayType ? char.class.arrayType() : char.class;
- break;
- case "short":
- parameters[i] = isArrayType ? short.class.arrayType() : short.class;
- break;
- case "int":
- parameters[i] = isArrayType ? int.class.arrayType() : int.class;
- break;
- case "long":
- parameters[i] = isArrayType ? long.class.arrayType() : long.class;
- break;
- case "float":
- parameters[i] = isArrayType ? float.class.arrayType() : float.class;
- break;
- case "double":
- parameters[i] = isArrayType ? double.class.arrayType() : double.class;
- break;
- default:
- parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType()
- : classLoader.loadClass(typeName);
- }
- }
- return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- throw new IllegalArgumentException(
- "The specified method cannot be found. Is this descriptor valid? "
- + descriptor, e);
+ ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
+ ret.containerPath = location.getContainerPath();
+ ret.containerOffset = location.getContainerOffset();
+ ret.methodOffset = location.getMethodOffset();
+ callback.onResult(ret);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to invoke result callback", e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 6d54be84d5e5..676c6fa4f138 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -53,6 +53,7 @@ import android.os.Handler;
import android.os.PowerExemptionManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
@@ -78,6 +79,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.BiFunction;
+import java.util.function.Supplier;
/**
* Helper class to send broadcasts for various situations.
@@ -216,7 +218,7 @@ public final class BroadcastHelper {
filterExtrasForReceiver, bOptions);
}
- void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
+ void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotSupplier,
boolean mediaStatus,
boolean replacing,
@NonNull String[] pkgNames,
@@ -236,7 +238,7 @@ public final class BroadcastHelper {
null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras),
+ snapshotSupplier, callingUid, intentExtras),
null /* bOptions */, null /* requiredPermissions */);
}
@@ -357,9 +359,14 @@ public final class BroadcastHelper {
@Nullable int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
@NonNull AndroidPackage pkg,
- @NonNull String[] sharedUidPackages) {
+ @NonNull String[] sharedUidPackages,
+ @NonNull String reasonForTrace) {
final boolean isForWholeApp = componentNames.contains(packageName);
if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
+ tracePackageChangedBroadcastEvent(
+ android.content.pm.Flags.reduceBroadcastsForComponentStateChanges(),
+ reasonForTrace, packageName, "<implicit>" /* targetPackageName */,
+ "whole" /* targetComponent */, componentNames.size());
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList,
null /* targetPackageName */, null /* requiredPermissions */);
@@ -381,6 +388,9 @@ public final class BroadcastHelper {
// First, send the PACKAGE_CHANGED broadcast to the system.
if (!TextUtils.equals(packageName, "android")) {
+ tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+ "android" /* targetPackageName */, "notExported" /* targetComponent */,
+ notExportedComponentNames.size());
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, "android" /* targetPackageName */,
@@ -389,6 +399,9 @@ public final class BroadcastHelper {
}
// Second, send the PACKAGE_CHANGED broadcast to the application itself.
+ tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+ packageName /* targetPackageName */, "notExported" /* targetComponent */,
+ notExportedComponentNames.size());
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, packageName /* targetPackageName */,
@@ -400,6 +413,9 @@ public final class BroadcastHelper {
if (TextUtils.equals(packageName, sharedPackage)) {
continue;
}
+ tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+ sharedPackage /* targetPackageName */, "notExported" /* targetComponent */,
+ notExportedComponentNames.size());
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, sharedPackage /* targetPackageName */,
@@ -409,6 +425,9 @@ public final class BroadcastHelper {
}
if (!exportedComponentNames.isEmpty()) {
+ tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+ "<implicit>" /* targetPackageName */, "exported" /* targetComponent */,
+ exportedComponentNames.size());
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
exportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, null /* targetPackageName */,
@@ -544,7 +563,7 @@ public final class BroadcastHelper {
});
}
- void sendPostInstallBroadcasts(@NonNull Computer snapshot,
+ void sendPostInstallBroadcasts(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull InstallRequest request,
@NonNull String packageName,
@NonNull String requiredPermissionControllerPackage,
@@ -567,8 +586,8 @@ public final class BroadcastHelper {
final int[] uids = new int[]{request.getRemovedInfo().mUid};
notifyResourcesChanged(
false /* mediaStatus */, true /* replacing */, pkgNames, uids);
- sendResourcesChangedBroadcast(
- snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ sendResourcesChangedBroadcast(snapshotSupplier,
+ false /* mediaStatus */, true /* replacing */, pkgNames, uids);
}
sendPackageRemovedBroadcasts(
request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/,
@@ -608,6 +627,7 @@ public final class BroadcastHelper {
null /* broadcastAllowList */, null);
}
+ final Computer snapshot = snapshotSupplier.get();
// Send installed broadcasts if the package is not a static shared lib.
if (staticSharedLibraryName == null) {
// Send PACKAGE_ADDED broadcast for users that see the package for the first time
@@ -732,7 +752,7 @@ public final class BroadcastHelper {
if (!isArchived) {
final String[] pkgNames = new String[]{packageName};
final int[] uids = new int[]{request.getPkg().getUid()};
- sendResourcesChangedBroadcast(snapshot,
+ sendResourcesChangedBroadcast(snapshotSupplier,
true /* mediaStatus */, true /* replacing */, pkgNames, uids);
notifyResourcesChanged(true /* mediaStatus */,
true /* replacing */, pkgNames, uids);
@@ -749,7 +769,8 @@ public final class BroadcastHelper {
sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
dontKillApp,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
- pkg.getUid(), null);
+ pkg.getUid(), null /* reason */,
+ "static_shared_library_changed" /* reasonForTrace */);
}
}
}
@@ -860,8 +881,8 @@ public final class BroadcastHelper {
* access all the packages in the extras.
*/
@Nullable
- private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
- @NonNull Bundle extras) {
+ private static Bundle filterExtrasChangedPackageList(
+ @NonNull Supplier<Computer> snapshotSupplier, int callingUid, @NonNull Bundle extras) {
if (UserHandle.isCore(callingUid)) {
// see all
return extras;
@@ -873,6 +894,7 @@ public final class BroadcastHelper {
final int userId = extras.getInt(
Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(callingUid));
final int[] uids = extras.getIntArray(Intent.EXTRA_CHANGED_UID_LIST);
+ final Computer snapshot = snapshotSupplier.get();
final Pair<String[], int[]> filteredPkgs =
filterPackages(snapshot, pkgs, uids, callingUid, userId);
if (ArrayUtils.isEmpty(filteredPkgs.first)) {
@@ -939,7 +961,8 @@ public final class BroadcastHelper {
boolean dontKillApp,
@NonNull ArrayList<String> componentNames,
int packageUid,
- @NonNull String reason) {
+ @NonNull String reason,
+ @NonNull String reasonForTrace) {
PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
Process.SYSTEM_UID);
if (setting == null || setting.getPkg() == null) {
@@ -952,10 +975,12 @@ public final class BroadcastHelper {
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
final SparseArray<int[]> broadcastAllowList =
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
+ final String[] sharedUserPackages =
+ snapshot.getSharedUserPackagesForPackage(packageName, userId);
mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
instantUserIds, broadcastAllowList, setting.getPkg(),
- snapshot.getSharedUserPackagesForPackage(packageName, userId)));
+ sharedUserPackages, reasonForTrace));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
}
@@ -1120,7 +1145,7 @@ public final class BroadcastHelper {
* @param uidList The uids of packages which have suspension changes.
* @param userId The user where packages reside.
*/
- void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot,
+ void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String intent,
@NonNull String[] pkgList,
@NonNull int[] uidList,
@@ -1138,7 +1163,7 @@ public final class BroadcastHelper {
.toBundle();
BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver =
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras);
+ snapshotSupplier, callingUid, intentExtras);
mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
extras, flags, null /* targetPkg */, null /* finishedReceiver */,
new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
@@ -1148,7 +1173,7 @@ public final class BroadcastHelper {
null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
}
- void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
+ void sendMyPackageSuspendedOrUnsuspended(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String[] affectedPackages,
boolean suspended,
int userId) {
@@ -1163,6 +1188,7 @@ public final class BroadcastHelper {
return;
}
final int[] targetUserIds = new int[] {userId};
+ final Computer snapshot = snapshotSupplier.get();
for (String packageName : affectedPackages) {
final Bundle appExtras = suspended
? SuspendPackageHelper.getSuspendedPackageAppExtras(
@@ -1192,7 +1218,7 @@ public final class BroadcastHelper {
* @param uidList The uids of packages which have suspension changes.
* @param userId The user where packages reside.
*/
- void sendDistractingPackagesChanged(@NonNull Computer snapshot,
+ void sendDistractingPackagesChanged(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String[] pkgList,
@NonNull int[] uidList,
int userId,
@@ -1208,11 +1234,11 @@ public final class BroadcastHelper {
null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras),
+ snapshotSupplier, callingUid, intentExtras),
null /* bOptions */, null /* requiredPermissions */));
}
- void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
+ void sendResourcesChangedBroadcastAndNotify(@NonNull Supplier<Computer> snapshotSupplier,
boolean mediaStatus,
boolean replacing,
@NonNull ArrayList<AndroidPackage> packages) {
@@ -1224,7 +1250,7 @@ public final class BroadcastHelper {
packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
- sendResourcesChangedBroadcast(snapshot, mediaStatus,
+ sendResourcesChangedBroadcast(snapshotSupplier, mediaStatus,
replacing, packageNames, packageUids);
notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
}
@@ -1247,4 +1273,25 @@ public final class BroadcastHelper {
mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
uids, mHandler);
}
+
+ private static void tracePackageChangedBroadcastEvent(boolean applyFlag,
+ @NonNull String reasonForTrace, @Nullable String packageName,
+ @Nullable String targetPackageName, @Nullable String targetComponent,
+ int componentSize) {
+
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ return;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("broadcastPackageChanged; ");
+ builder.append("af="); builder.append(applyFlag);
+ builder.append(",rft="); builder.append(reasonForTrace);
+ builder.append(",pn="); builder.append(packageName);
+ builder.append(",tpn="); builder.append(targetPackageName);
+ builder.append(",tc="); builder.append(targetComponent);
+ builder.append(",cs="); builder.append(componentSize);
+
+ Trace.instant(Trace.TRACE_TAG_SYSTEM_SERVER, builder.toString());
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 58b1e496f5f1..be2f58dc276c 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -138,7 +138,8 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerInternal;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerLocal;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -5851,10 +5852,10 @@ public class ComputerEngine implements Computer {
if (isHotword) {
return true;
}
- OnDeviceIntelligenceManagerInternal onDeviceIntelligenceManagerInternal =
- mInjector.getLocalService(OnDeviceIntelligenceManagerInternal.class);
- return onDeviceIntelligenceManagerInternal != null
- && uid == onDeviceIntelligenceManagerInternal.getInferenceServiceUid();
+ OnDeviceIntelligenceManagerLocal onDeviceIntelligenceManagerLocal =
+ LocalManagerRegistry.getManager(OnDeviceIntelligenceManagerLocal.class);
+ return onDeviceIntelligenceManagerLocal != null
+ && uid == onDeviceIntelligenceManagerLocal.getInferenceServiceUid();
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index c5ec73b4e2b8..c4e981d487bc 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -123,7 +123,7 @@ public final class DistractingPackageHelper {
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm::snapshotComputer,
changedPackages, changedUids.toArray(), userId, restrictionFlags);
mPm.scheduleWritePackageRestrictions(userId);
}
@@ -198,7 +198,7 @@ public final class DistractingPackageHelper {
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
- mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm::snapshotComputer,
packageArray, changedUids.toArray(), userId, RESTRICTION_NONE);
mPm.scheduleWritePackageRestrictions(userId);
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 673a1021cfe1..81bb92946dd9 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.app.role.RoleManager.ROLE_SYSTEM_DEPENDENCY_INSTALLER;
import static android.content.pm.PackageInstaller.ACTION_INSTALL_DEPENDENCY;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.os.Process.SYSTEM_UID;
@@ -56,8 +57,6 @@ import java.util.concurrent.TimeUnit;
public class InstallDependencyHelper {
private static final String TAG = InstallDependencyHelper.class.getSimpleName();
private static final boolean DEBUG = true;
- private static final String ROLE_SYSTEM_DEPENDENCY_INSTALLER =
- "android.app.role.SYSTEM_DEPENDENCY_INSTALLER";
// The maximum amount of time to wait before the system unbinds from the verifier.
private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1);
@@ -78,24 +77,22 @@ public class InstallDependencyHelper {
mPackageInstallerService = packageInstallerService;
}
- void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId,
- Handler handler, OutcomeReceiver<Void, PackageManagerException> origCallback) {
+ void resolveLibraryDependenciesIfNeeded(List<SharedLibraryInfo> missingLibraries,
+ PackageLite pkg, Computer snapshot, int userId, Handler handler,
+ OutcomeReceiver<Void, PackageManagerException> origCallback) {
CallOnceProxy callback = new CallOnceProxy(handler, origCallback);
try {
- resolveLibraryDependenciesIfNeededInternal(pkg, snapshot, userId, handler, callback);
- } catch (PackageManagerException e) {
- callback.onError(e);
+ resolveLibraryDependenciesIfNeededInternal(
+ missingLibraries, pkg, snapshot, userId, handler, callback);
} catch (Exception e) {
onError(callback, e.getMessage());
}
}
- private void resolveLibraryDependenciesIfNeededInternal(PackageLite pkg, Computer snapshot,
- int userId, Handler handler, CallOnceProxy callback) throws PackageManagerException {
- final List<SharedLibraryInfo> missing =
- mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
-
+ private void resolveLibraryDependenciesIfNeededInternal(List<SharedLibraryInfo> missing,
+ PackageLite pkg, Computer snapshot, int userId, Handler handler,
+ CallOnceProxy callback) {
if (missing.isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No missing dependency for " + pkg.getPackageName());
@@ -129,14 +126,19 @@ public class InstallDependencyHelper {
}
}
- void notifySessionComplete(int sessionId, boolean success) {
+ List<SharedLibraryInfo> getMissingSharedLibraries(PackageLite pkg)
+ throws PackageManagerException {
+ return mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
+ }
+
+ void notifySessionComplete(int sessionId) {
if (DEBUG) {
- Slog.d(TAG, "Session complete for " + sessionId + " result: " + success);
+ Slog.d(TAG, "Session complete for " + sessionId);
}
synchronized (mTrackers) {
List<DependencyInstallTracker> completedTrackers = new ArrayList<>();
for (DependencyInstallTracker tracker: mTrackers) {
- if (!tracker.onSessionComplete(sessionId, success)) {
+ if (!tracker.onSessionComplete(sessionId)) {
completedTrackers.add(tracker);
}
}
@@ -354,7 +356,7 @@ public class InstallDependencyHelper {
// Don't wait for sessions that finished already
if (sessionInfo == null) {
Binder.withCleanCallingIdentity(() -> {
- notifySessionComplete(sessionId, /*success=*/ true);
+ notifySessionComplete(sessionId);
});
}
}
@@ -459,19 +461,13 @@ public class InstallDependencyHelper {
*
* Returns true if we still need to continue tracking.
*/
- public boolean onSessionComplete(int sessionId, boolean success) {
+ public boolean onSessionComplete(int sessionId) {
synchronized (this) {
if (!mPendingSessionIds.contains(sessionId)) {
// This had no impact on tracker, so continue tracking
return true;
}
- if (!success) {
- // If one of the dependency fails, the orig session would fail too.
- onError(mCallback, "Failed to install all dependencies");
- return false; // No point in tracking anymore
- }
-
mPendingSessionIds.remove(sessionId);
if (mPendingSessionIds.isEmpty()) {
mCallback.onResult(null);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 69c6ce8ea0cd..b48b39c2edd5 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2983,7 +2983,7 @@ final class InstallPackageHelper {
}
}
- public void sendPendingBroadcasts() {
+ public void sendPendingBroadcasts(String reasonForTrace) {
String[] packages;
ArrayList<String>[] components;
int numBroadcasts = 0, numUsers;
@@ -3027,7 +3027,8 @@ final class InstallPackageHelper {
// Send broadcasts
for (int i = 0; i < numBroadcasts; i++) {
mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i],
- true /* dontKillApp */, components[i], uids[i], null /* reason */);
+ true /* dontKillApp */, components[i], uids[i], null /* reason */,
+ reasonForTrace);
}
}
@@ -3084,7 +3085,7 @@ final class InstallPackageHelper {
mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
}
- mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName,
+ mBroadcastHelper.sendPostInstallBroadcasts(mPm::snapshotComputer, request, packageName,
mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages,
mPm.mRequiredInstallerPackage,
/* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index b0fe3a97af6e..c96c160deb0f 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -170,6 +170,8 @@ final class InstallRequest {
private final boolean mHasAppMetadataFileFromInstaller;
private boolean mKeepArtProfile = false;
+ private final boolean mDependencyInstallerEnabled;
+ private final int mMissingSharedLibraryCount;
// New install
InstallRequest(InstallingSession params) {
@@ -190,6 +192,8 @@ final class InstallRequest {
mRequireUserAction = params.mRequireUserAction;
mPreVerifiedDomains = params.mPreVerifiedDomains;
mHasAppMetadataFileFromInstaller = params.mHasAppMetadataFile;
+ mDependencyInstallerEnabled = params.mDependencyInstallerEnabled;
+ mMissingSharedLibraryCount = params.mMissingSharedLibraryCount;
}
// Install existing package as user
@@ -209,6 +213,8 @@ final class InstallRequest {
mInstallerUidForInstallExisting = installerUid;
mSystem = isSystem;
mHasAppMetadataFileFromInstaller = false;
+ mDependencyInstallerEnabled = false;
+ mMissingSharedLibraryCount = 0;
}
// addForInit
@@ -231,6 +237,8 @@ final class InstallRequest {
mRequireUserAction = USER_ACTION_UNSPECIFIED;
mDisabledPs = disabledPs;
mHasAppMetadataFileFromInstaller = false;
+ mDependencyInstallerEnabled = false;
+ mMissingSharedLibraryCount = 0;
}
@Nullable
@@ -1069,4 +1077,12 @@ final class InstallRequest {
boolean isKeepArtProfile() {
return mKeepArtProfile;
}
+
+ int getMissingSharedLibraryCount() {
+ return mMissingSharedLibraryCount;
+ }
+
+ boolean isDependencyInstallerEnabled() {
+ return mDependencyInstallerEnabled;
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index ccc117566989..6a2bf83ba368 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -103,6 +103,8 @@ class InstallingSession {
final DomainSet mPreVerifiedDomains;
final boolean mHasAppMetadataFile;
@Nullable final String mDexoptCompilerFilter;
+ final boolean mDependencyInstallerEnabled;
+ final int mMissingSharedLibraryCount;
// For move install
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
@@ -138,13 +140,16 @@ class InstallingSession {
mPreVerifiedDomains = null;
mHasAppMetadataFile = false;
mDexoptCompilerFilter = null;
+ mDependencyInstallerEnabled = false;
+ mMissingSharedLibraryCount = 0;
}
InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
UserHandle user, SigningDetails signingDetails, int installerUid,
PackageLite packageLite, DomainSet preVerifiedDomains, PackageManagerService pm,
- boolean hasAppMetadatafile) {
+ boolean hasAppMetadatafile, boolean dependencyInstallerEnabled,
+ int missingSharedLibraryCount) {
mPm = pm;
mUser = user;
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
@@ -175,6 +180,8 @@ class InstallingSession {
mPreVerifiedDomains = preVerifiedDomains;
mHasAppMetadataFile = hasAppMetadatafile;
mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter;
+ mDependencyInstallerEnabled = dependencyInstallerEnabled;
+ mMissingSharedLibraryCount = missingSharedLibraryCount;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 4ea405441030..0a067048be42 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -76,7 +76,7 @@ final class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
case SEND_PENDING_BROADCAST: {
- mPm.sendPendingBroadcasts();
+ mPm.sendPendingBroadcasts((String) msg.obj);
break;
}
case POST_INSTALL: {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 516b002885af..e1fcc6650650 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -2330,8 +2330,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
if (Flags.sdkDependencyInstaller()) {
- mInstallDependencyHelper.notifySessionComplete(
- session.sessionId, success);
+ mInstallDependencyHelper.notifySessionComplete(session.sessionId);
}
final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 891d66a5d238..c6760431116e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -31,6 +31,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STOR
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
@@ -109,6 +110,7 @@ import android.content.pm.PackageInstaller.UserActionReason;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
@@ -540,6 +542,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private DomainSet mPreVerifiedDomains;
+ private AtomicBoolean mDependencyInstallerEnabled = new AtomicBoolean();
+ private AtomicInteger mMissingSharedLibraryCount = new AtomicInteger();
+
static class FileEntry {
private final int mIndex;
private final InstallationFile mFile;
@@ -3232,6 +3237,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (Flags.sdkDependencyInstaller()
&& params.isAutoInstallDependenciesEnabled
&& !isMultiPackage()) {
+ mDependencyInstallerEnabled.set(true);
resolveLibraryDependenciesIfNeeded();
} else {
install();
@@ -3241,8 +3247,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private void resolveLibraryDependenciesIfNeeded() {
synchronized (mLock) {
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite,
- mPm.snapshotComputer(), userId, mHandler,
+ List<SharedLibraryInfo> missingLibraries = new ArrayList<>();
+ try {
+ missingLibraries = mInstallDependencyHelper.getMissingSharedLibraries(mPackageLite);
+ } catch (PackageManagerException e) {
+ handleDependencyResolutionFailure(e);
+ } catch (Exception e) {
+ handleDependencyResolutionFailure(
+ new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage()));
+ }
+
+ mMissingSharedLibraryCount.set(missingLibraries.size());
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(missingLibraries,
+ mPackageLite, mPm.snapshotComputer(), userId, mHandler,
new OutcomeReceiver<>() {
@Override
@@ -3252,14 +3270,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void onError(@NonNull PackageManagerException e) {
- final String completeMsg = ExceptionUtils.getCompleteMessage(e);
- setSessionFailed(e.error, completeMsg);
- onSessionDependencyResolveFailure(e.error, completeMsg);
+ handleDependencyResolutionFailure(e);
}
});
}
}
+ private void handleDependencyResolutionFailure(@NonNull PackageManagerException e) {
+ final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+ setSessionFailed(e.error, completeMsg);
+ onSessionDependencyResolveFailure(e.error, completeMsg);
+ PackageMetrics.onDependencyInstallationFailure(
+ sessionId, getPackageName(), e.error, mInstallerUid, params,
+ mMissingSharedLibraryCount.get());
+ }
+
/**
* Stages this session for install and returns a
* {@link InstallingSession} representing this new staged state.
@@ -3327,7 +3352,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
return new InstallingSession(sessionId, stageDir, localObserver, params, mInstallSource,
user, mSigningDetails, mInstallerUid, mPackageLite, mPreVerifiedDomains, mPm,
- mHasAppMetadataFile);
+ mHasAppMetadataFile, mDependencyInstallerEnabled.get(),
+ mMissingSharedLibraryCount.get());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 65bb701563a8..a0bbc454c10b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3020,6 +3020,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
}
+ public void updateMetricsIfNeeded() {
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ if (displayManager != null) {
+ final Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ if (display != null) {
+ display.getMetrics(mMetrics);
+ }
+ }
+ }
+
private void notifyPackageUseInternal(String packageName, int reason) {
long time = System.currentTimeMillis();
synchronized (mLock) {
@@ -3531,7 +3541,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mPendingBroadcasts.addComponents(userId, packageName, updatedComponents);
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
- mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(SEND_PENDING_BROADCAST,
+ "reset_component_state_changed" /* obj */),
+ BROADCAST_DELAY);
}
}
@@ -3828,7 +3841,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mPendingBroadcasts.addComponent(userId, componentPkgName, componentName.getClassName());
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
- mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(SEND_PENDING_BROADCAST,
+ "component_label_icon_changed" /* obj */), BROADCAST_DELAY);
}
}
@@ -4063,6 +4077,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mPendingBroadcasts.remove(userId, packageName);
} else {
mPendingBroadcasts.addComponent(userId, packageName, componentName);
+ Trace.instant(Trace.TRACE_TAG_PACKAGE_MANAGER, "setEnabledSetting broadcast: "
+ + componentName + ": " + setting.getEnabledState());
scheduleBroadcastMessage = true;
}
}
@@ -4085,7 +4101,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay
? BROADCAST_DELAY
: BROADCAST_DELAY_DURING_STARTUP;
- mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(SEND_PENDING_BROADCAST,
+ "component_state_changed" /* obj */), broadcastDelay);
}
}
}
@@ -4103,7 +4120,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final int packageUid = UserHandle.getUid(
userId, pkgSettings.get(packageName).getAppId());
mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName,
- false /* dontKillApp */, components, packageUid, null /* reason */);
+ false /* dontKillApp */, components, packageUid, null /* reason */,
+ "component_state_changed" /* reasonForTrace */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -4331,7 +4349,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
true /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(),
- Intent.ACTION_OVERLAY_CHANGED);
+ Intent.ACTION_OVERLAY_CHANGED, "overlay_changed" /* reasonForTrace */);
}
}, overlayFilter);
@@ -6382,7 +6400,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (pkgUserState != null && pkgUserState.isInstalled()) {
final int packageUid = UserHandle.getUid(userIds[i], appId);
mBroadcastHelper.sendPackageChangedBroadcast(snapShot, packageName,
- true /* dontKillApp */, components, packageUid, reason);
+ true /* dontKillApp */, components, packageUid, reason,
+ "mime_group_changed" /* reasonForTrace */);
}
}
});
@@ -8177,8 +8196,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath);
}
- void sendPendingBroadcasts() {
- mInstallPackageHelper.sendPendingBroadcasts();
+ void sendPendingBroadcasts(String reasonForTrace) {
+ mInstallPackageHelper.sendPendingBroadcasts(reasonForTrace);
}
void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 975758241e77..7af39f74d0d6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -749,7 +749,7 @@ public class PackageManagerServiceUtils {
null /*abiOverride*/, false /*isIncremental*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
+ + "; pkg: " + packageName + "; err: " + e.getMessage());
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 0acadb129f2b..856d6a726da5 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -32,7 +32,9 @@ import android.app.admin.SecurityLog;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.DataLoaderType;
import android.content.pm.Flags;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -173,7 +175,10 @@ final class PackageMetrics {
mInstallRequest.isInstallInherit() /* is_inherit */,
mInstallRequest.isInstallForUsers() /* is_installing_existing_as_user */,
mInstallRequest.isInstallMove() /* is_move_install */,
- false /* is_staged */
+ false /* is_staged */,
+ mInstallRequest
+ .isDependencyInstallerEnabled() /* is_install_dependencies_enabled */,
+ mInstallRequest.getMissingSharedLibraryCount() /* missing_dependencies_count */
);
}
@@ -323,7 +328,53 @@ final class PackageMetrics {
verifyingSession.isInherit() /* is_inherit */,
false /* is_installing_existing_as_user */,
false /* is_move_install */,
- verifyingSession.isStaged() /* is_staged */
+ verifyingSession.isStaged() /* is_staged */,
+ false /* is_install_dependencies_enabled */,
+ 0 /* missing_dependencies_count */
+ );
+ }
+
+ static void onDependencyInstallationFailure(
+ int sessionId, String packageName, int errorCode, int installerPackageUid,
+ PackageInstaller.SessionParams params, int missingDependenciesCount) {
+ if (params == null) {
+ return;
+ }
+ int dataLoaderType = DataLoaderType.NONE;
+ if (params.dataLoaderParams != null) {
+ dataLoaderType = params.dataLoaderParams.getType();
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLATION_SESSION_REPORTED,
+ sessionId /* session_id */,
+ packageName /* package_name */,
+ INVALID_UID /* uid */,
+ null /* user_ids */,
+ null /* user_types */,
+ null /* original_user_ids */,
+ null /* original_user_types */,
+ errorCode /* public_return_code */,
+ 0 /* internal_error_code */,
+ 0 /* apks_size_bytes */,
+ 0 /* version_code */,
+ null /* install_steps */,
+ null /* step_duration_millis */,
+ 0 /* total_duration_millis */,
+ 0 /* install_flags */,
+ installerPackageUid /* installer_package_uid */,
+ INVALID_UID /* original_installer_package_uid */,
+ dataLoaderType /* data_loader_type */,
+ params.requireUserAction /* user_action_required_type */,
+ (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 /* is_instant */,
+ false /* is_replace */,
+ false /* is_system */,
+ params.mode
+ == PackageInstaller.SessionParams.MODE_INHERIT_EXISTING /* is_inherit */,
+ false /* is_installing_existing_as_user */,
+ false /* is_move_install */,
+ params.isStaged /* is_staged */,
+ true /* is_install_dependencies_enabled */,
+ missingDependenciesCount /* missing_dependencies_count */
);
}
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 4b82de048a4a..ef49f49cf040 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -34,6 +34,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -196,21 +197,13 @@ public class PackageMonitorCallbackHelper {
private void doNotifyCallbacksByIntent(Intent intent, int userId,
int[] broadcastAllowList, Handler handler) {
- RemoteCallbackList<IRemoteCallback> callbacks;
- synchronized (mLock) {
- callbacks = mCallbacks;
- }
- doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler,
+ doNotifyCallbacks(intent, userId, broadcastAllowList, handler,
null /* filterExtrasFunction */);
}
private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
SparseArray<int[]> broadcastAllowList, Handler handler,
BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
- RemoteCallbackList<IRemoteCallback> callbacks;
- synchronized (mLock) {
- callbacks = mCallbacks;
- }
for (int userId : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
@@ -226,48 +219,58 @@ public class PackageMonitorCallbackHelper {
final int[] allowUids =
broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
- doNotifyCallbacks(callbacks, intent, userId, allowUids, handler, filterExtrasFunction);
+ doNotifyCallbacks(intent, userId, allowUids, handler, filterExtrasFunction);
}
}
- private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
- Intent intent, int userId, int[] allowUids, Handler handler,
+ private void doNotifyCallbacks(Intent intent, int userId, int[] allowUids, Handler handler,
BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
- handler.post(() -> callbacks.broadcast((callback, user) -> {
- RegisterUser registerUser = (RegisterUser) user;
- if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
- != userId)) {
- return;
- }
- int registerUid = registerUser.getUid();
- if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
- && !ArrayUtils.contains(allowUids, registerUid)) {
- if (DEBUG) {
- Slog.w(TAG, "Skip invoke PackageMonitorCallback for " + intent.getAction()
- + ", uid " + registerUid);
- }
- return;
- }
- Intent newIntent = intent;
- if (filterExtrasFunction != null) {
- final Bundle extras = intent.getExtras();
- if (extras != null) {
- final Bundle filteredExtras = filterExtrasFunction.apply(registerUid, extras);
- if (filteredExtras == null) {
- // caller is unable to access this intent
+ handler.post(() -> {
+ final ArrayList<Pair<IRemoteCallback, Intent>> target = new ArrayList<>();
+ synchronized (mLock) {
+ mCallbacks.broadcast((callback, user) -> {
+ RegisterUser registerUser = (RegisterUser) user;
+ if ((registerUser.getUserId() != UserHandle.USER_ALL)
+ && (registerUser.getUserId() != userId)) {
+ return;
+ }
+ int registerUid = registerUser.getUid();
+ if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
+ && !ArrayUtils.contains(allowUids, registerUid)) {
if (DEBUG) {
- Slog.w(TAG,
- "Skip invoke PackageMonitorCallback for " + intent.getAction()
- + " because null filteredExtras");
+ Slog.w(TAG, "Skip invoke PackageMonitorCallback for "
+ + intent.getAction() + ", uid " + registerUid);
}
return;
}
- newIntent = new Intent(newIntent);
- newIntent.replaceExtras(filteredExtras);
- }
+ Intent newIntent = intent;
+ if (filterExtrasFunction != null) {
+ final Bundle extras = intent.getExtras();
+ if (extras != null) {
+ final Bundle filteredExtras =
+ filterExtrasFunction.apply(registerUid, extras);
+ if (filteredExtras == null) {
+ // caller is unable to access this intent
+ if (DEBUG) {
+ Slog.w(TAG,
+ "Skip invoke PackageMonitorCallback for "
+ + intent.getAction()
+ + " because null filteredExtras");
+ }
+ return;
+ }
+ newIntent = new Intent(newIntent);
+ newIntent.replaceExtras(filteredExtras);
+ }
+ }
+ target.add(new Pair<>(callback, newIntent));
+ });
+ }
+ for (int i = 0; i < target.size(); i++) {
+ Pair<IRemoteCallback, Intent> p = target.get(i);
+ invokeCallback(p.first, p.second);
}
- invokeCallback(callback, newIntent);
- }));
+ });
}
private void invokeCallback(IRemoteCallback callback, Intent intent) {
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index 08047695a42a..38075c135d2a 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -65,6 +65,7 @@ public class RestrictionsSet {
throw new IllegalArgumentException("empty restriction bundle cannot be added.");
}
mUserRestrictions.put(userId, restrictions);
+ UserManager.invalidateUserRestriction();
}
/**
@@ -84,6 +85,7 @@ public class RestrictionsSet {
} else {
mUserRestrictions.delete(userId);
}
+ UserManager.invalidateUserRestriction();
return true;
}
@@ -102,6 +104,9 @@ public class RestrictionsSet {
removed = true;
}
}
+ if (removed) {
+ UserManager.invalidateUserRestriction();
+ }
return removed;
}
@@ -129,6 +134,7 @@ public class RestrictionsSet {
i--;
}
}
+ UserManager.invalidateUserRestriction();
}
}
@@ -192,6 +198,7 @@ public class RestrictionsSet {
public boolean remove(@UserIdInt int userId) {
boolean hasUserRestriction = mUserRestrictions.contains(userId);
mUserRestrictions.remove(userId);
+ UserManager.invalidateUserRestriction();
return hasUserRestriction;
}
@@ -200,6 +207,7 @@ public class RestrictionsSet {
*/
public void removeAllRestrictions() {
mUserRestrictions.clear();
+ UserManager.invalidateUserRestriction();
}
/**
diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java
index 854e142c7c2f..ec91da90729b 100644
--- a/services/core/java/com/android/server/pm/SaferIntentUtils.java
+++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java
@@ -213,6 +213,7 @@ public class SaferIntentUtils {
* CTS tests. The code in this method shall properly avoid control flows using these arguments.
*/
public static void blockNullAction(IntentArgs args, List componentList) {
+ if (args.intent.getAction() != null) return;
if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return;
final Computer computer = (Computer) args.snapshot;
@@ -235,14 +236,11 @@ public class SaferIntentUtils {
}
final ParsedMainComponent comp = infoToComponent(
resolveInfo.getComponentInfo(), resolver, args.isReceiver);
- if (comp != null && !comp.getIntents().isEmpty()
- && args.intent.getAction() == null) {
+ if (comp != null && !comp.getIntents().isEmpty()) {
match = false;
}
} else if (c instanceof IntentFilter) {
- if (args.intent.getAction() == null) {
- match = false;
- }
+ match = false;
}
if (!match) {
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 951986fbd71a..a09d4776d986 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -227,7 +227,7 @@ public final class StorageEventHelper extends StorageEventListener {
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm::snapshotComputer,
true /* mediaStatus */, false /* replacing */, loaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.add(vol.getId());
@@ -279,7 +279,7 @@ public final class StorageEventHelper extends StorageEventListener {
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm::snapshotComputer,
false /* mediaStatus */, false /* replacing */, unloaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.remove(vol.getId());
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 4e70cc52ef90..88fd1aa159d3 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -192,21 +192,20 @@ public final class SuspendPackageHelper {
}
});
- final Computer newSnapshot = mPm.snapshotComputer();
if (!notifyPackagesList.isEmpty()) {
final String[] changedPackages =
notifyPackagesList.toArray(new String[0]);
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
changedPackages, notifyUids.toArray(), quarantined, targetUserId);
- mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
- suspended, targetUserId);
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
+ changedPackages, suspended, targetUserId);
mPm.scheduleWritePackageRestrictions(targetUserId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
targetUserId);
@@ -343,13 +342,12 @@ public final class SuspendPackageHelper {
});
mPm.scheduleWritePackageRestrictions(targetUserId);
- final Computer newSnapshot = mPm.snapshotComputer();
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
- mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
- false, targetUserId);
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
+ packageArray, false, targetUserId);
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
Intent.ACTION_PACKAGES_UNSUSPENDED,
packageArray, unsuspendedUids.toArray(), false, targetUserId);
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 94b49e538621..1fda4782fc86 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -165,6 +165,22 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 041f2d3a459d..04ce4e692fef 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -68,6 +68,10 @@ class UserDataPreparer {
void prepareUserData(UserInfo userInfo, int flags) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ if (storage == null) {
+ Log.e(TAG, "prepareUserData failed due to unable get StorageManager");
+ return;
+ }
/*
* Internal storage must be prepared before adoptable storage, since the user's volume
* keys are stored in their internal storage.
@@ -159,14 +163,16 @@ class UserDataPreparer {
void destroyUserData(int userId, int flags) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
- /*
- * Volume destruction order isn't really important, but to avoid any weird issues we
- * process internal storage last, the opposite of prepareUserData.
- */
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- if (volumeUuid != null) {
- destroyUserDataLI(volumeUuid, userId, flags);
+ if (storage != null) {
+ /*
+ * Volume destruction order isn't really important, but to avoid any weird issues we
+ * process internal storage last, the opposite of prepareUserData.
+ */
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ if (volumeUuid != null) {
+ destroyUserDataLI(volumeUuid, userId, flags);
+ }
}
}
destroyUserDataLI(null /* internal storage */, userId, flags);
@@ -194,9 +200,10 @@ class UserDataPreparer {
}
}
- // All the user's data directories should be empty now, so finish the job.
- storage.destroyUserStorage(volumeUuid, userId, flags);
-
+ if (storage != null) {
+ // All the user's data directories should be empty now, so finish the job.
+ storage.destroyUserStorage(volumeUuid, userId, flags);
+ }
} catch (Exception e) {
logCriticalInfo(Log.WARN,
"Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 066fce068d61..8249d65868cd 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1113,6 +1113,7 @@ public class UserManagerService extends IUserManager.Stub {
UserManager.invalidateUserPropertiesCache();
}
UserManager.invalidateCacheOnUserListChange();
+ UserManager.invalidateUserRestriction();
}
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 542ae8eb9207..dd60a155f2fb 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -16,11 +16,7 @@
package com.android.server.pm;
-import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageInstaller.SessionParams.MODE_INHERIT_EXISTING;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
@@ -40,9 +36,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.DataLoaderType;
@@ -68,7 +62,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
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..e9cb279439a6 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -40,7 +40,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
@@ -351,22 +351,22 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Override
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, Integer, SyncNotedAppOp> superImpl) {
+ @Nullable String message, boolean shouldCollectMessage,
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl) {
if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 09feb18d07bf..6ab30595e46b 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -216,12 +216,12 @@ final class DefaultPermissionGrantPolicy {
private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
static {
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+
if (Flags.replaceBodySensorPermissionEnabled()) {
SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE);
SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
- } else {
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 05bc69a9f1f0..672eb4caf798 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -264,6 +264,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
persistentDeviceId, mPermissionManagerServiceImpl::checkUidPermission);
}
+ @Override
+ @Context.PermissionRequestState
+ public int getPermissionRequestState(@NonNull String packageName,
+ @NonNull String permissionName, int deviceId) {
+ Objects.requireNonNull(permissionName, "permission can't be null.");
+ Objects.requireNonNull(packageName, "package name can't be null.");
+ return mPermissionManagerServiceImpl.getPermissionRequestState(packageName, permissionName,
+ getPersistentDeviceId(deviceId));
+ }
+
private String getPersistentDeviceId(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index ea71953d7cb3..ca70bddc5ac1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1014,6 +1014,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ throw new IllegalStateException("getPermissionRequestState is not supported.");
+ }
+
+ @Override
public Map<String, PermissionManager.PermissionState> getAllPermissionStates(
@NonNull String packageName, @NonNull String deviceId, int userId) {
throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 754b141ff10d..b607832767a1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -20,6 +20,7 @@ import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -407,6 +408,16 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
int checkUidPermission(int uid, String permName, String deviceId);
/**
+ * Returns one of the permission state
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}
+ * for permission request permission flow.
+ */
+ int getPermissionRequestState(@NonNull String packageName, @NonNull String permName,
+ @NonNull String deviceId);
+
+ /**
* Gets the permission states for requested package, persistent device and user.
* <p>
* <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index c18f856594ed..ba5e97e7b113 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -247,6 +247,13 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ Log.i(LOG_TAG, "checkUidPermissionState(permName = " + permName + ", deviceId = "
+ + deviceId + ", packageName = " + packageName + ")");
+ return mService.getPermissionRequestState(packageName, permName, deviceId);
+ }
+
+ @Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
Log.i(LOG_TAG,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 40139baf0e98..008c14db8b65 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -319,6 +319,11 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ return mNewImplementation.getPermissionRequestState(packageName, permName, deviceId);
+ }
+
+ @Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
return mNewImplementation.getAllPermissionStates(packageName, deviceId, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 981d3d92b15a..2a47f51da951 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -345,6 +345,18 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
}
+
+ @Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ Trace.traceBegin(TRACE_TAG,
+ "TaggedTracingPermissionManagerServiceImpl#checkUidPermissionState");
+ try {
+ return mService.getPermissionRequestState(packageName, permName, deviceId);
+ } finally {
+ Trace.traceEnd(TRACE_TAG);
+ }
+ }
+
@Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 33210e28281e..3f9144f0d980 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -53,7 +53,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
@@ -136,7 +136,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
- locationManagerInternal.setLocationPackageTagsListener(
+ if (locationManagerInternal != null) {
+ locationManagerInternal.setLocationPackageTagsListener(
(uid, packageTagsList) -> {
synchronized (mLock) {
if (packageTagsList.isEmpty()) {
@@ -158,6 +159,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
mLocationTags);
}
});
+ }
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -246,12 +248,11 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String,
- Integer, Boolean, String, Boolean, Integer, SyncNotedAppOp> superImpl) {
+ boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String,
+ Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
diff --git a/services/core/java/com/android/server/policy/EventLogTags.logtags b/services/core/java/com/android/server/policy/EventLogTags.logtags
index 75633820d01f..a4b6472fbe62 100644
--- a/services/core/java/com/android/server/policy/EventLogTags.logtags
+++ b/services/core/java/com/android/server/policy/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.server.policy
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7c4d4253157e..c4d1cc723804 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3576,7 +3576,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
case KeyEvent.KEYCODE_I:
- if (firstDown && event.isMetaPressed()) {
+ if (firstDown && event.isMetaPressed() && isUserSetupComplete() && !keyguardOn) {
showSystemSettings();
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
@@ -4081,6 +4081,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
@@ -4105,6 +4106,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
@@ -4119,18 +4121,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
.isAccessibilityShortcutAvailable(false);
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
return enableTalkbackAndMagnifierKeyGestures();
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
- return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl();
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
- return InputSettings.isAccessibilityBounceKeysFeatureEnabled()
- && keyboardA11yShortcutControl();
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
- return InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl();
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
- return InputSettings.isAccessibilityStickyKeysFeatureEnabled()
- && keyboardA11yShortcutControl();
default:
return false;
}
@@ -4148,6 +4138,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int displayId = event.getDisplayId();
int modifierState = event.getModifierState();
boolean keyguardOn = keyguardOn();
+ boolean canLaunchApp = isUserSetupComplete() && !keyguardOn;
switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
if (complete) {
@@ -4164,7 +4155,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
- if (complete) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
+ if (complete && canLaunchApp) {
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
deviceId, SystemClock.uptimeMillis(),
AssistUtils.INVOCATION_TYPE_UNKNOWN);
@@ -4177,7 +4169,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
- if (complete) {
+ if (complete && canLaunchApp) {
showSystemSettings();
}
return true;
@@ -4280,7 +4272,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
- if (complete) {
+ if (complete && canLaunchApp) {
launchTargetSearchActivity();
}
return true;
@@ -4361,63 +4353,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
AppLaunchData data = event.getAppLaunchData();
- if (complete && isUserSetupComplete() && !keyguardOn
- && data != null && mModifierShortcutManager.launchApplication(data)) {
+ if (complete && canLaunchApp && data != null
+ && mModifierShortcutManager.launchApplication(data)) {
dismissKeyboardShortcutsMenu();
}
return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
- if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
- && keyboardA11yShortcutControl()) {
- if (complete) {
- final boolean bounceKeysEnabled =
- InputSettings.isAccessibilityBounceKeysEnabled(
- mContext);
- InputSettings.setAccessibilityBounceKeysThreshold(mContext,
- bounceKeysEnabled ? 0
- : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
- }
- return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
+ NotificationManager nm = getNotificationService();
+ if (nm != null) {
+ boolean isEnabled = nm.getZenMode() != Settings.Global.ZEN_MODE_OFF;
+ nm.setZenMode(isEnabled ? Settings.Global.ZEN_MODE_OFF
+ : Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ "Key gesture DND", true);
}
- break;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
- if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl()) {
- if (complete) {
- final boolean mouseKeysEnabled =
- InputSettings.isAccessibilityMouseKeysEnabled(
- mContext);
- InputSettings.setAccessibilityMouseKeysEnabled(mContext,
- !mouseKeysEnabled);
- }
- return true;
- }
- break;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
- if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
- && keyboardA11yShortcutControl()) {
- if (complete) {
- final boolean stickyKeysEnabled =
- InputSettings.isAccessibilityStickyKeysEnabled(mContext);
- InputSettings.setAccessibilityStickyKeysEnabled(mContext,
- !stickyKeysEnabled);
- }
- return true;
- }
- break;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
- if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl()) {
- if (complete) {
- final boolean slowKeysEnabled =
- InputSettings.isAccessibilitySlowKeysEnabled(mContext);
- InputSettings.setAccessibilitySlowKeysThreshold(mContext,
- slowKeysEnabled ? 0
- : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
- }
- return true;
- }
- break;
+ return true;
}
return false;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 36bc0b93cd7c..ce8dc69e4b26 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -7093,7 +7093,11 @@ public final class PowerManagerService extends SystemService
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) {
if (mFoldGracePeriodProvider.isEnabled()) {
if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+ Slog.d(TAG, "Showing dismissible keyguard");
mNotifier.showDismissibleKeyguard();
+ } else {
+ Slog.i(TAG, "There is a screen wake lock present: "
+ + "sleep request will be ignored");
}
continue; // never actually goes to sleep for SOFT_SLEEP
} else {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 987a84994451..aae7417970eb 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -80,6 +80,7 @@ import com.android.server.utils.Slogf;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -296,7 +297,11 @@ public final class HintManagerService extends SystemService {
mPowerHalVersion = 0;
mUsesFmq = false;
if (mPowerHal != null) {
- mSupportInfo = getSupportInfo();
+ try {
+ mSupportInfo = getSupportInfo();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Could not contact PowerHAL!", e);
+ }
}
mDefaultCpuHeadroomCalculationWindowMillis =
new CpuHeadroomParamsInternal().calculationWindowMillis;
@@ -314,7 +319,7 @@ public final class HintManagerService extends SystemService {
}
}
- SupportInfo getSupportInfo() {
+ SupportInfo getSupportInfo() throws RemoteException {
try {
mPowerHalVersion = mPowerHal.getInterfaceVersion();
if (mPowerHalVersion >= 6) {
@@ -325,9 +330,42 @@ public final class HintManagerService extends SystemService {
}
SupportInfo supportInfo = new SupportInfo();
+ supportInfo.usesSessions = isHintSessionSupported();
+ // Global boosts & modes aren't currently relevant for HMS clients
+ supportInfo.boosts = 0;
+ supportInfo.modes = 0;
+ supportInfo.sessionHints = 0;
+ supportInfo.sessionModes = 0;
+ supportInfo.sessionTags = 0;
+
supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
supportInfo.headroom.isCpuSupported = false;
supportInfo.headroom.isGpuSupported = false;
+
+ supportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo();
+ if (isHintSessionSupported()) {
+ if (mPowerHalVersion == 4) {
+ // Assume we support the V4 hints & modes unless specified
+ // otherwise; this is to avoid breaking backwards compat
+ // since we historically just assumed they were.
+ supportInfo.sessionHints = 31; // first 5 bits are ones
+ }
+ if (mPowerHalVersion == 5) {
+ // Assume we support the V5 hints & modes unless specified
+ // otherwise; this is to avoid breaking backwards compat
+ // since we historically just assumed they were.
+
+ // Hal V5 has 8 modes, all of which it assumes are supported,
+ // so we represent that by having the first 8 bits set
+ supportInfo.sessionHints = 255; // first 8 bits are ones
+ // Hal V5 has 1 mode which it assumes is supported, so we
+ // represent that by having the first bit set
+ supportInfo.sessionModes = 1;
+ // Hal V5 has 5 tags, all of which it assumes are supported,
+ // so we represent that by having the first 5 bits set
+ supportInfo.sessionTags = 31;
+ }
+ }
return supportInfo;
}
@@ -1228,7 +1266,7 @@ public final class HintManagerService extends SystemService {
@SessionTag int tag, SessionCreationConfig creationConfig,
SessionConfig config) {
if (!isHintSessionSupported()) {
- throw new UnsupportedOperationException("PowerHAL is not supported!");
+ throw new UnsupportedOperationException("PowerHintSessions are not supported!");
}
java.util.Objects.requireNonNull(token);
@@ -1424,12 +1462,6 @@ public final class HintManagerService extends SystemService {
removeChannelItem(callingTgid, callingUid);
};
- @Override
- public long getHintSessionPreferredRate() {
- return mHintSessionPreferredRate;
- }
-
- @Override
public int getMaxGraphicsPipelineThreadsCount() {
return MAX_GRAPHICS_PIPELINE_THREADS_COUNT;
}
@@ -1451,6 +1483,7 @@ public final class HintManagerService extends SystemService {
if (!mSupportInfo.headroom.isCpuSupported) {
throw new UnsupportedOperationException();
}
+ checkCpuHeadroomParams(params);
final CpuHeadroomParams halParams = new CpuHeadroomParams();
halParams.tids = new int[]{Binder.getCallingPid()};
halParams.calculationType = params.calculationType;
@@ -1458,10 +1491,6 @@ public final class HintManagerService extends SystemService {
if (params.usesDeviceHeadroom) {
halParams.tids = new int[]{};
} else if (params.tids != null && params.tids.length > 0) {
- if (params.tids.length > 5) {
- throw new IllegalArgumentException(
- "More than 5 TIDs is requested: " + params.tids.length);
- }
if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) {
final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid());
for (int tid : params.tids) {
@@ -1501,11 +1530,45 @@ public final class HintManagerService extends SystemService {
}
}
+ private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) {
+ boolean calculationTypeMatched = false;
+ try {
+ for (final Field field :
+ CpuHeadroomParams.CalculationType.class.getDeclaredFields()) {
+ if (field.getType() == byte.class) {
+ byte value = field.getByte(null);
+ if (value == params.calculationType) {
+ calculationTypeMatched = true;
+ break;
+ }
+ }
+ }
+ } catch (IllegalAccessException e) {
+ Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed");
+ }
+ if (!calculationTypeMatched) {
+ throw new IllegalArgumentException(
+ "Unknown CPU headroom calculation type " + (int) params.calculationType);
+ }
+ if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+ throw new IllegalArgumentException(
+ "Invalid CPU headroom calculation window, expected [50, 10000] but got "
+ + params.calculationWindowMillis);
+ }
+ if (!params.usesDeviceHeadroom) {
+ if (params.tids != null && params.tids.length > 5) {
+ throw new IllegalArgumentException(
+ "More than 5 TIDs requested: " + params.tids.length);
+ }
+ }
+ }
+
@Override
public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) {
if (!mSupportInfo.headroom.isGpuSupported) {
throw new UnsupportedOperationException();
}
+ checkGpuHeadroomParams(params);
final GpuHeadroomParams halParams = new GpuHeadroomParams();
halParams.calculationType = params.calculationType;
halParams.calculationWindowMillis = params.calculationWindowMillis;
@@ -1536,6 +1599,33 @@ public final class HintManagerService extends SystemService {
}
}
+ private void checkGpuHeadroomParams(GpuHeadroomParamsInternal params) {
+ boolean calculationTypeMatched = false;
+ try {
+ for (final Field field :
+ GpuHeadroomParams.CalculationType.class.getDeclaredFields()) {
+ if (field.getType() == byte.class) {
+ byte value = field.getByte(null);
+ if (value == params.calculationType) {
+ calculationTypeMatched = true;
+ break;
+ }
+ }
+ }
+ } catch (IllegalAccessException e) {
+ Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed");
+ }
+ if (!calculationTypeMatched) {
+ throw new IllegalArgumentException(
+ "Unknown GPU headroom calculation type " + (int) params.calculationType);
+ }
+ if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+ throw new IllegalArgumentException(
+ "Invalid GPU headroom calculation window, expected [50, 10000] but got "
+ + params.calculationWindowMillis);
+ }
+ }
+
@Override
public long getCpuHeadroomMinIntervalMillis() {
if (!mSupportInfo.headroom.isCpuSupported) {
@@ -1562,13 +1652,24 @@ public final class HintManagerService extends SystemService {
}
@Override
+ public IHintManager.HintManagerClientData
+ registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) {
+ IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData();
+ out.preferredRateNanos = mHintSessionPreferredRate;
+ out.maxGraphicsPipelineThreads = getMaxGraphicsPipelineThreadsCount();
+ out.powerHalVersion = mPowerHalVersion;
+ out.supportInfo = mSupportInfo;
+ return out;
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate);
pw.println("MaxGraphicsPipelineThreadsCount: " + MAX_GRAPHICS_PIPELINE_THREADS_COUNT);
- pw.println("HAL Support: " + isHintSessionSupported());
+ pw.println("Hint Session Support: " + isHintSessionSupported());
pw.println("Active Sessions:");
synchronized (mLock) {
for (int i = 0; i < mActiveSessions.size(); i++) {
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 6f1810711b3a..fe14f6b172f1 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -5723,7 +5723,9 @@ public class BatteryStatsImpl extends BatteryStats {
displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
- case Display.STATE_OFF: // fallthrough
+ case Display.STATE_OFF:
+ shouldScheduleSync = true;
+ break;
case Display.STATE_UNKNOWN:
// Not tracked by timers.
break;
@@ -5756,7 +5758,9 @@ public class BatteryStatsImpl extends BatteryStats {
displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
- case Display.STATE_OFF: // fallthrough
+ case Display.STATE_OFF:
+ shouldScheduleSync = true;
+ break;
case Display.STATE_UNKNOWN:
// Not tracked by timers.
break;
@@ -5873,7 +5877,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (shouldScheduleSync) {
if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
- mScreenPowerStatsCollector.schedule();
+ mScreenPowerStatsCollector.onScreenStateChange();
} else {
final int numDisplays = mPerDisplayBatteryStats.length;
final int[] displayStates = new int[numDisplays];
@@ -11443,7 +11447,7 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiPowerStatsCollector.addConsumer(this::recordPowerStats);
mBluetoothPowerStatsCollector = new BluetoothPowerStatsCollector(
- mPowerStatsCollectorInjector);
+ mPowerStatsCollectorInjector, this::onBluetoothPowerStatsRetrieved);
mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats);
mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector);
@@ -13413,6 +13417,13 @@ public class BatteryStatsImpl extends BatteryStats {
private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
= new BluetoothActivityInfoCache();
+ private void onBluetoothPowerStatsRetrieved(BluetoothActivityEnergyInfo info,
+ long elapsedRealtimeMs, long uptimeMs) {
+ // Do not populate consumed energy, because energy attribution is done by
+ // BluetoothPowerStatsProcessor.
+ updateBluetoothStateLocked(info, POWER_DATA_UNAVAILABLE, elapsedRealtimeMs, uptimeMs);
+ }
+
/**
* Distribute Bluetooth energy info and network traffic to apps.
*
@@ -13421,10 +13432,6 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
- if (mBluetoothPowerStatsCollector.isEnabled()) {
- return;
- }
-
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating bluetooth stats: " + info);
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 63e8d9973237..8c588b4c9b98 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -195,23 +195,22 @@ public class BatteryUsageStatsProvider {
mLastAccumulationMonotonicHistorySize = historySize;
}
- handler.post(() -> accumulateBatteryUsageStats(stats));
+ // No need to store the accumulated stats asynchronously, as the entire accumulation
+ // operation is async
+ handler.post(() -> accumulateBatteryUsageStats(stats, false));
}
/**
* Computes BatteryUsageStats for the period since the last accumulated stats were stored,
- * adds them to the accumulated stats and saves the result.
+ * adds them to the accumulated stats and asynchronously saves the result.
*/
public void accumulateBatteryUsageStats(BatteryStatsImpl stats) {
- AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
+ accumulateBatteryUsageStats(stats, true);
+ }
- final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includeProcessStateData()
- .includePowerStateData()
- .includeScreenStateData()
- .build();
- updateAccumulatedBatteryUsageStats(accumulatedStats, stats, query);
+ private void accumulateBatteryUsageStats(BatteryStatsImpl stats, boolean storeAsync) {
+ AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
+ updateAccumulatedBatteryUsageStats(accumulatedStats, stats);
PowerStatsSpan powerStatsSpan = new PowerStatsSpan(AccumulatedBatteryUsageStatsSection.ID);
powerStatsSpan.addSection(
@@ -220,8 +219,13 @@ public class BatteryUsageStatsProvider {
accumulatedStats.startWallClockTime,
accumulatedStats.endMonotonicTime - accumulatedStats.startMonotonicTime);
mMonotonicClock.write();
- mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
- accumulatedStats.builder::discard);
+ if (storeAsync) {
+ mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
+ accumulatedStats.builder::discard);
+ } else {
+ mPowerStatsStore.storePowerStatsSpan(powerStatsSpan);
+ accumulatedStats.builder.discard();
+ }
}
/**
@@ -269,7 +273,7 @@ public class BatteryUsageStatsProvider {
BatteryUsageStats batteryUsageStats;
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) {
- batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query, currentTimeMs);
+ batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query);
} else if (query.getAggregatedToTimestamp() == 0) {
BatteryUsageStats.Builder builder = computeBatteryUsageStats(stats, query,
query.getMonotonicStartTime(),
@@ -288,9 +292,13 @@ public class BatteryUsageStatsProvider {
}
private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats,
- BatteryUsageStatsQuery query, long currentTimeMs) {
+ BatteryUsageStatsQuery query) {
AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
- updateAccumulatedBatteryUsageStats(accumulatedStats, stats, query);
+ if (accumulatedStats.endMonotonicTime == MonotonicClock.UNDEFINED
+ || mMonotonicClock.monotonicTime() - accumulatedStats.endMonotonicTime
+ > query.getMaxStatsAge()) {
+ updateAccumulatedBatteryUsageStats(accumulatedStats, stats);
+ }
return accumulatedStats.builder.build();
}
@@ -321,7 +329,7 @@ public class BatteryUsageStatsProvider {
}
private void updateAccumulatedBatteryUsageStats(AccumulatedBatteryUsageStats accumulatedStats,
- BatteryStatsImpl stats, BatteryUsageStatsQuery query) {
+ BatteryStatsImpl stats) {
long startMonotonicTime = accumulatedStats.endMonotonicTime;
if (startMonotonicTime == MonotonicClock.UNDEFINED) {
startMonotonicTime = stats.getMonotonicStartTime();
@@ -333,6 +341,7 @@ public class BatteryUsageStatsProvider {
accumulatedStats.builder = new BatteryUsageStats.Builder(
stats.getCustomEnergyConsumerNames(), true, true, true, 0);
accumulatedStats.startWallClockTime = stats.getStartClockTime();
+ accumulatedStats.startMonotonicTime = stats.getMonotonicStartTime();
accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime);
}
@@ -342,7 +351,7 @@ public class BatteryUsageStatsProvider {
accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, stats.getHistory(),
- startMonotonicTime, MonotonicClock.UNDEFINED);
+ startMonotonicTime, endMonotonicTime);
populateGeneralInfo(accumulatedStats.builder, stats);
}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
index d7aa9876fe0d..c12ae63140c9 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
@@ -15,6 +15,7 @@
*/
package com.android.server.power.stats;
+import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
@@ -41,7 +42,10 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
private static final long BLUETOOTH_ACTIVITY_REQUEST_TIMEOUT = 20000;
- private static final long ENERGY_UNSPECIFIED = -1;
+ interface Observer {
+ void onBluetoothPowerStatsRetrieved(@Nullable BluetoothActivityEnergyInfo info,
+ long elapsedRealtimeMs, long uptimeMs);
+ }
public interface BluetoothStatsRetriever {
interface Callback {
@@ -65,6 +69,7 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
}
private final Injector mInjector;
+ private final Observer mObserver;
private com.android.server.power.stats.format.BluetoothPowerStatsLayout mLayout;
private boolean mIsInitialized;
@@ -89,13 +94,14 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
- public BluetoothPowerStatsCollector(Injector injector) {
+ public BluetoothPowerStatsCollector(Injector injector, @Nullable Observer observer) {
super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
BatteryConsumer.powerComponentIdToString(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH)),
injector.getUidResolver(),
injector.getClock());
mInjector = injector;
+ mObserver = observer;
}
@Override
@@ -146,15 +152,20 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
Arrays.fill(mDeviceStats, 0);
mPowerStats.uidStats.clear();
- collectBluetoothActivityInfo();
+ BluetoothActivityEnergyInfo activityInfo = collectBluetoothActivityInfo();
collectBluetoothScanStats();
mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
+ if (mObserver != null) {
+ mObserver.onBluetoothPowerStatsRetrieved(activityInfo, mClock.elapsedRealtime(),
+ mClock.uptimeMillis());
+ }
+
return mPowerStats;
}
- private void collectBluetoothActivityInfo() {
+ private BluetoothActivityEnergyInfo collectBluetoothActivityInfo() {
CompletableFuture<BluetoothActivityEnergyInfo> immediateFuture = new CompletableFuture<>();
boolean success = mBluetoothStatsRetriever.requestControllerActivityEnergyInfo(
Runnable::run,
@@ -173,7 +184,7 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
});
if (!success) {
- return;
+ return null;
}
BluetoothActivityEnergyInfo activityInfo;
@@ -186,7 +197,7 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
}
if (activityInfo == null) {
- return;
+ return null;
}
long rxTime = activityInfo.getControllerRxTimeMillis();
@@ -241,6 +252,8 @@ public class BluetoothPowerStatsCollector extends PowerStatsCollector {
mLayout.setUidTxBytes(stats, txDelta);
}
}
+
+ return activityInfo;
}
private void collectBluetoothScanStats() {
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
index c38904fe2873..90039e8e704e 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
@@ -111,6 +111,22 @@ public class ScreenPowerStatsCollector extends PowerStatsCollector {
return true;
}
+ /**
+ * Must be called whenever the screen state (on/off/doze) changes.
+ */
+ public void onScreenStateChange() {
+ if (ensureInitialized() && mConsumedEnergyHelper.getEnergyConsumerCount() != 0) {
+ // Sync power monitor reading immediately, because the estimation algorithm
+ // distributes consumed power proportionally between screen states.
+ // Since screen power consumption differs dramatically between different states,
+ // this would lead an overestimation in the screen-off state.
+ forceSchedule();
+ return;
+ }
+ // Perhaps schedule a sync, allowing throttling
+ schedule();
+ }
+
@Override
public PowerStats collectStats() {
if (!ensureInitialized()) {
@@ -126,7 +142,7 @@ public class ScreenPowerStatsCollector extends PowerStatsCollector {
long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
if (!mFirstSample) {
mLayout.setScreenOnDuration(mPowerStats.stats, display,
- screenOnTimeMs - mLastScreenOnTime[display]);
+ Math.max(0, screenOnTimeMs - mLastScreenOnTime[display]));
}
mLastScreenOnTime[display] = screenOnTimeMs;
@@ -135,14 +151,15 @@ public class ScreenPowerStatsCollector extends PowerStatsCollector {
mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(display, level);
if (!mFirstSample) {
mLayout.setBrightnessLevelDuration(mPowerStats.stats, display, level,
- brightnessLevelTimeMs - mLastBrightnessLevelTime[display][level]);
+ Math.max(0, brightnessLevelTimeMs
+ - mLastBrightnessLevelTime[display][level]));
}
mLastBrightnessLevelTime[display][level] = brightnessLevelTimeMs;
}
long screenDozeTimeMs = mScreenUsageTimeRetriever.getScreenDozeTimeMs(display);
if (!mFirstSample) {
mLayout.setScreenDozeDuration(mPowerStats.stats, display,
- screenDozeTimeMs - mLastDozeTime[display]);
+ Math.max(0, screenDozeTimeMs - mLastDozeTime[display]));
}
mLastDozeTime[display] = screenDozeTimeMs;
}
@@ -162,7 +179,7 @@ public class ScreenPowerStatsCollector extends PowerStatsCollector {
}
mLayout.setUidTopActivityDuration(uidStats,
- mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration);
+ Math.max(0, mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration));
});
long elapsedRealtime = mClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
index dcdd3bd8b3fa..1b864bbe479c 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
@@ -23,6 +23,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
import java.util.function.Consumer;
@@ -169,10 +170,15 @@ public class PowerStatsAggregator {
}
}
}
- if (lastTime > baseTime) {
- mStats.setDuration(lastTime - baseTime);
- mStats.finish(lastTime);
- consumer.accept(mStats);
+ if (startedSession) {
+ if (endTimeMs != MonotonicClock.UNDEFINED) {
+ lastTime = endTimeMs;
+ }
+ if (lastTime > baseTime) {
+ mStats.setDuration(lastTime - baseTime);
+ mStats.finish(lastTime);
+ consumer.accept(mStats);
+ }
}
mStats.reset(); // to free up memory
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e753ce845ddc..1bed48a09d9e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -163,6 +163,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
private final RollbackPackageHealthObserver mPackageHealthObserver;
private final AppDataRollbackHelper mAppDataRollbackHelper;
private final Runnable mRunExpiration = this::runExpiration;
+ private final PackageWatchdog mPackageWatchdog;
// The # of milli-seconds to sleep for each received ACTION_PACKAGE_ENABLE_ROLLBACK.
// Used by #blockRollbackManager to test timeout in enabling rollbacks.
@@ -190,6 +191,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
+ mPackageWatchdog = PackageWatchdog.getInstance(mContext);
// Kick off and start monitoring the handler thread.
HandlerThread handlerThread = new HandlerThread("RollbackManagerServiceHandler");
@@ -1249,12 +1251,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
// should document in PackageInstaller.SessionParams#setEnableRollback
// After enabling and committing any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
- mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
+ rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
}
} else {
- mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
+ rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
}
runExpiration();
}
@@ -1317,7 +1319,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
});
- PackageWatchdog.getInstance(mContext).dump(ipw);
+ mPackageWatchdog.dump(ipw);
}
@AnyThread
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
index b9c8d3dc5319..acdea881e3d1 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
@@ -24,9 +24,14 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserManager;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook {
private static final String TAG = "AdvancedProtectionDisallowCellular2G";
@@ -35,13 +40,15 @@ public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProt
new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G);
private final DevicePolicyManager mDevicePolicyManager;
private final TelephonyManager mTelephonyManager;
+ private final SubscriptionManager mSubscriptionManager;
public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) {
super(context, enabled);
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
- setPolicy(enabled);
+ onAdvancedProtectionChanged(enabled);
}
@NonNull
@@ -50,14 +57,45 @@ public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProt
return mFeature;
}
+ private static boolean isEmbeddedSubscriptionVisible(SubscriptionInfo subInfo) {
+ if (subInfo.isEmbedded()
+ && (subInfo.getProfileClass() == SubscriptionManager.PROFILE_CLASS_PROVISIONING
+ || (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()
+ && subInfo.isOnlyNonTerrestrialNetwork()))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private List<TelephonyManager> getActiveTelephonyManagers() {
+ List<TelephonyManager> telephonyManagers = new ArrayList<>();
+
+ for (SubscriptionInfo subInfo : mSubscriptionManager.getActiveSubscriptionInfoList()) {
+ if (isEmbeddedSubscriptionVisible(subInfo)) {
+ telephonyManagers.add(
+ mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()));
+ }
+ }
+
+ return telephonyManagers;
+ }
+
@Override
public boolean isAvailable() {
- return mTelephonyManager.isDataCapable();
- }
+ for (TelephonyManager telephonyManager : getActiveTelephonyManagers()) {
+ if (telephonyManager.isDataCapable()
+ && telephonyManager.isRadioInterfaceCapabilitySupported(
+ mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)) {
+ return true;
+ }
+ }
- private void setPolicy(boolean enabled) {
- Slog.i(TAG, "setPolicy called with " + enabled);
+ return false;
+ }
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
if (enabled) {
Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction");
mDevicePolicyManager.addUserRestrictionGlobally(
@@ -68,19 +106,4 @@ public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProt
ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G);
}
}
-
- @Override
- public void onAdvancedProtectionChanged(boolean enabled) {
- setPolicy(enabled);
-
- // Leave 2G disabled even if APM is disabled.
- if (!enabled) {
- long oldAllowedTypes =
- mTelephonyManager.getAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
- long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
- mTelephonyManager.setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes);
- }
- }
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
index bb523d63c43a..59bb34d1bedd 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
@@ -19,24 +19,13 @@ package com.android.server.security.advancedprotection.features;
import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
-import android.Manifest;
import android.annotation.NonNull;
-import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.UserManager;
import android.security.advancedprotection.AdvancedProtectionFeature;
import android.util.Slog;
-import com.android.server.LocalServices;
-
/** @hide */
public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
extends AdvancedProtectionHook {
@@ -45,24 +34,14 @@ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature(
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
- private final ActivityManagerInternal mActivityManagerInternal;
- private final AppOpsManager mAppOpsManager;
private final DevicePolicyManager mDevicePolicyManager;
- private final IPackageManager mIPackageManager;
- private final PackageManager mPackageManager;
- private final UserManager mUserManager;
public DisallowInstallUnknownSourcesAdvancedProtectionHook(@NonNull Context context,
boolean enabled) {
super(context, enabled);
- mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- mIPackageManager = AppGlobals.getPackageManager();
- mUserManager = context.getSystemService(UserManager.class);
- mPackageManager = context.getPackageManager();
- setRestriction(enabled);
+ onAdvancedProtectionChanged(enabled);
}
@NonNull
@@ -76,7 +55,8 @@ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
return true;
}
- private void setRestriction(boolean enabled) {
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
if (enabled) {
Slog.d(TAG, "Setting DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
mDevicePolicyManager.addUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
@@ -87,36 +67,4 @@ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
}
}
-
- @Override
- public void onAdvancedProtectionChanged(boolean enabled) {
- setRestriction(enabled);
- if (enabled) return;
-
- // Leave OP_REQUEST_INSTALL_PACKAGES disabled when APM is disabled.
- Slog.d(TAG, "Setting all OP_REQUEST_INSTALL_PACKAGES to MODE_ERRORED");
- for (UserInfo userInfo : mUserManager.getAliveUsers()) {
- try {
- final String[] packagesWithRequestInstallPermission = mIPackageManager
- .getAppOpPermissionPackages(
- Manifest.permission.REQUEST_INSTALL_PACKAGES, userInfo.id);
- for (String packageName : packagesWithRequestInstallPermission) {
- try {
- int uid = mPackageManager.getPackageUidAsUser(packageName, userInfo.id);
- boolean isCallerInstrumented = mActivityManagerInternal
- .getInstrumentationSourceUid(uid) != Process.INVALID_UID;
- if (!isCallerInstrumented) {
- mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
- packageName, AppOpsManager.MODE_ERRORED);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Couldn't retrieve uid for a package: " + e);
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't retrieve packages with REQUEST_INSTALL_PACKAGES."
- + " getAppOpPermissionPackages() threw the following exception: " + e);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
index 0ea88e8523f0..687442b47fb3 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
@@ -28,6 +28,7 @@ import com.android.server.ServiceThread;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
public class DataAggregator {
private static final String TAG = "IntrusionDetection DataAggregator";
@@ -36,11 +37,10 @@ public class DataAggregator {
private static final int MSG_DISABLE = 2;
private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
- private static final IntrusionDetectionAdminReceiver ADMIN_RECEIVER =
- new IntrusionDetectionAdminReceiver();
private final IntrusionDetectionService mIntrusionDetectionService;
private final ArrayList<DataSource> mDataSources;
+ private final AtomicBoolean mIsLoggingInitialized = new AtomicBoolean(false);
private Context mContext;
private List<IntrusionDetectionEvent> mStoredEvents = new ArrayList<>();
@@ -59,30 +59,20 @@ public class DataAggregator {
mHandler = new EventHandler(looper, this);
}
- /**
- * Initialize DataSources
- * @return Whether the initialization succeeds.
- */
- public boolean initialize() {
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this);
- mDataSources.add(securityLogSource);
-
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, this);
- ADMIN_RECEIVER.setNetworkLogEventCallback(networkLogSource);
- mDataSources.add(networkLogSource);
-
- for (DataSource ds : mDataSources) {
- if (!ds.initialize()) {
- return false;
- }
- }
- return true;
+ /** Initialize DataSources */
+ private void initialize() {
+ mDataSources.add(new SecurityLogSource(mContext, this));
+ mDataSources.add(new NetworkLogSource(mContext, this));
}
/**
* Enable the data collection of all DataSources.
*/
public void enable() {
+ if (!mIsLoggingInitialized.get()) {
+ initialize();
+ mIsLoggingInitialized.set(true);
+ }
mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
/* allowIo */ false);
mHandlerThread.start();
@@ -111,9 +101,6 @@ public class DataAggregator {
*/
public void disable() {
mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
- for (DataSource ds : mDataSources) {
- ds.disable();
- }
}
private void onNewSingleData(IntrusionDetectionEvent event) {
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
index 61fac46be82d..0bc448245b76 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
@@ -18,11 +18,6 @@ package com.android.server.security.intrusiondetection;
public interface DataSource {
/**
- * Initialize the data source.
- */
- boolean initialize();
-
- /**
* Enable the data collection.
*/
void enable();
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java
deleted file mode 100644
index dba7374fe02a..000000000000
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java
+++ /dev/null
@@ -1,42 +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.server.security.intrusiondetection;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Slog;
-
-public class IntrusionDetectionAdminReceiver extends DeviceAdminReceiver {
- private static final String TAG = "IntrusionDetectionAdminReceiver";
-
- private static NetworkLogSource sNetworkLogSource;
-
- @Override
- public void onNetworkLogsAvailable(
- Context context, Intent intent, long batchToken, int networkLogsCount) {
- if (sNetworkLogSource != null) {
- sNetworkLogSource.onNetworkLogsAvailable(batchToken);
- } else {
- Slog.w(TAG, "Network log receiver is not initialized");
- }
- }
-
- public void setNetworkLogEventCallback(NetworkLogSource networkLogSource) {
- sNetworkLogSource = networkLogSource;
- }
-}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
index b25656ebf47f..a16e66de09a9 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
@@ -24,42 +24,56 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.intrusiondetection.IIntrusionDetectionEventTransport;
import android.security.intrusiondetection.IntrusionDetectionEvent;
+import android.security.intrusiondetection.IIntrusionDetectionEventTransport;
import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.infra.AndroidFuture;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.Process;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
public class IntrusionDetectionEventTransportConnection implements ServiceConnection {
+ private static final String PRODUCTION_BUILD = "user";
+ private static final String PROPERTY_BUILD_TYPE = "ro.build.type";
+ private static final String PROPERTY_INTRUSION_DETECTION_SERVICE_NAME =
+ "debug.intrusiondetection_package_name";
+ private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 min
private static final String TAG = "IntrusionDetectionEventTransportConnection";
- private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins
private final Context mContext;
private String mIntrusionDetectionEventTransportConfig;
volatile IIntrusionDetectionEventTransport mService;
+
public IntrusionDetectionEventTransportConnection(Context context) {
mContext = context;
- mService = null;
}
/**
* Initialize the IntrusionDetectionEventTransport binder service.
- * @return Whether the initialization succeed.
+ *
+ * @return Whether the initialization succeeds.
*/
public boolean initialize() {
+ Slog.d(TAG, "initialize");
if (!bindService()) {
return false;
}
+ // Wait for the service to be connected before calling initialize.
+ waitForConnection();
AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
try {
mService.initialize(resultFuture);
@@ -77,6 +91,20 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec
}
}
+ private void waitForConnection() {
+ synchronized (this) {
+ while (mService == null) {
+ Slog.d(TAG, "waiting for connection to service...");
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ /* never interrupted */
+ }
+ }
+ Slog.d(TAG, "connected to service");
+ }
+ }
+
/**
* Add data to the IntrusionDetectionEventTransport binder service.
* @param data List of IntrusionDetectionEvent.
@@ -118,11 +146,42 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec
}
}
+ private String getSystemPropertyValue(String propertyName) {
+ String commandString = "getprop " + propertyName;
+ try {
+ Process process = Runtime.getRuntime().exec(commandString);
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String propertyValue = reader.readLine();
+ reader.close();
+ return propertyValue;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to get system property value:", e);
+ return null;
+ }
+ }
+
private boolean bindService() {
- mIntrusionDetectionEventTransportConfig = mContext.getString(
- com.android.internal.R.string.config_intrusionDetectionEventTransport);
+ String buildType = getSystemPropertyValue(PROPERTY_BUILD_TYPE);
+ mIntrusionDetectionEventTransportConfig =
+ mContext.getString(
+ com.android.internal.R.string.config_intrusionDetectionEventTransport);
+
+ // If the build type is not production, and a property value is set, use it instead.
+ // This allows us to test the service with a different config.
+ if (!buildType.equals(PRODUCTION_BUILD)
+ && !TextUtils.isEmpty(
+ getSystemPropertyValue(PROPERTY_INTRUSION_DETECTION_SERVICE_NAME))) {
+ mIntrusionDetectionEventTransportConfig =
+ getSystemPropertyValue(PROPERTY_INTRUSION_DETECTION_SERVICE_NAME);
+ }
+ Slog.d(
+ TAG,
+ "mIntrusionDetectionEventTransportConfig: "
+ + mIntrusionDetectionEventTransportConfig);
+
if (TextUtils.isEmpty(mIntrusionDetectionEventTransportConfig)) {
- Slog.e(TAG, "config_intrusionDetectionEventTransport is empty");
+ Slog.e(TAG, "Unable to find a valid config for the transport service");
return false;
}
@@ -163,11 +222,19 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IIntrusionDetectionEventTransport.Stub.asInterface(service);
+ synchronized (this) {
+ mService = IIntrusionDetectionEventTransport.Stub.asInterface(service);
+ Slog.d(TAG, "connected to service");
+ this.notifyAll();
+ }
}
@Override
public void onServiceDisconnected(ComponentName name) {
- mService = null;
+ synchronized (this) {
+ mService = null;
+ Slog.d(TAG, "disconnected from service");
+ this.notifyAll();
+ }
}
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
index 0287b415b9c2..8ff1c7f22ffb 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
@@ -232,12 +232,10 @@ public class IntrusionDetectionService extends SystemService {
return;
}
- // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest.
- // Enable it when the transport component is ready.
- // if (!mIntrusionDetectionEventTransportConnection.initialize()) {
- // callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
- // return;
- // }
+ if (!mIntrusionDetectionEventTransportConnection.initialize()) {
+ callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
+ return;
+ }
mDataAggregator.enable();
mState = STATE_ENABLED;
@@ -252,9 +250,7 @@ public class IntrusionDetectionService extends SystemService {
return;
}
- // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest.
- // Enable it when the transport component is ready.
- // mIntrusionDetectionEventTransportConnection.release();
+ mIntrusionDetectionEventTransportConnection.release();
mDataAggregator.disable();
mState = STATE_DISABLED;
notifyStateMonitors();
diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
index 1c93d3f9c6a1..083b1fd61c46 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
@@ -17,118 +17,131 @@
package com.android.server.security.intrusiondetection;
import android.app.admin.ConnectEvent;
-import android.app.admin.DevicePolicyManager;
import android.app.admin.DnsEvent;
-import android.app.admin.NetworkEvent;
-import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.net.metrics.IpConnectivityLog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.security.intrusiondetection.IntrusionDetectionEvent;
import android.util.Slog;
-import java.util.List;
-import java.util.stream.Collectors;
+import com.android.server.LocalServices;
+import com.android.server.net.BaseNetdEventCallback;
+
+import java.util.concurrent.atomic.AtomicBoolean;
public class NetworkLogSource implements DataSource {
private static final String TAG = "IntrusionDetectionEvent NetworkLogSource";
+ private final AtomicBoolean mIsNetworkLoggingEnabled = new AtomicBoolean(false);
+ private final PackageManagerInternal mPm;
- private DevicePolicyManager mDpm;
- private ComponentName mAdmin;
private DataAggregator mDataAggregator;
- public NetworkLogSource(Context context, DataAggregator dataAggregator) {
+ private IIpConnectivityMetrics mIpConnectivityMetrics;
+ private long mId;
+
+ public NetworkLogSource(Context context, DataAggregator dataAggregator)
+ throws SecurityException {
mDataAggregator = dataAggregator;
- mDpm = context.getSystemService(DevicePolicyManager.class);
- mAdmin = new ComponentName(context, IntrusionDetectionAdminReceiver.class);
+ mPm = LocalServices.getService(PackageManagerInternal.class);
+ mId = 0;
+ initIpConnectivityMetrics();
}
- @Override
- public boolean initialize() {
- try {
- if (!mDpm.isAdminActive(mAdmin)) {
- Slog.e(TAG, "Admin " + mAdmin.flattenToString() + "is not active admin");
- return false;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "Security exception in initialize: ", e);
- return false;
- }
- return true;
+ private void initIpConnectivityMetrics() {
+ mIpConnectivityMetrics =
+ (IIpConnectivityMetrics)
+ IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
}
@Override
public void enable() {
- enableNetworkLog();
- }
-
- @Override
- public void disable() {
- disableNetworkLog();
- }
-
- private void enableNetworkLog() {
- if (!isNetworkLogEnabled()) {
- mDpm.setNetworkLoggingEnabled(mAdmin, true);
+ if (mIsNetworkLoggingEnabled.get()) {
+ Slog.w(TAG, "Network logging is already enabled");
+ return;
}
- }
-
- private void disableNetworkLog() {
- if (isNetworkLogEnabled()) {
- mDpm.setNetworkLoggingEnabled(mAdmin, false);
+ try {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
+ mIsNetworkLoggingEnabled.set(true);
+ } else {
+ Slog.e(TAG, "Failed to enable network logging; invalid callback");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to enable network logging; ", e);
}
}
- private boolean isNetworkLogEnabled() {
- return mDpm.isNetworkLoggingEnabled(mAdmin);
- }
-
- /**
- * Retrieve network logs when onNetworkLogsAvailable callback is received.
- *
- * @param batchToken The token representing the current batch of network logs.
- */
- public void onNetworkLogsAvailable(long batchToken) {
- List<NetworkEvent> events;
- try {
- events = mDpm.retrieveNetworkLogs(mAdmin, batchToken);
- } catch (SecurityException e) {
- Slog.e(
- TAG,
- "Admin "
- + mAdmin.flattenToString()
- + "does not have permission to retrieve network logs",
- e);
+ @Override
+ public void disable() {
+ if (!mIsNetworkLoggingEnabled.get()) {
+ Slog.w(TAG, "Network logging is already disabled");
return;
}
- if (events == null) {
- if (!isNetworkLogEnabled()) {
- Slog.w(TAG, "Network logging is disabled");
+ try {
+ if (!mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST)) {
+
+ mIsNetworkLoggingEnabled.set(false);
} else {
- Slog.e(TAG, "Invalid batch token: " + batchToken);
+ Slog.e(TAG, "Failed to enable network logging; invalid callback");
}
- return;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to disable network logging; ", e);
}
-
- List<IntrusionDetectionEvent> intrusionDetectionEvents =
- events.stream()
- .filter(event -> event != null)
- .map(event -> toIntrusionDetectionEvent(event))
- .collect(Collectors.toList());
- mDataAggregator.addBatchData(intrusionDetectionEvents);
}
- private IntrusionDetectionEvent toIntrusionDetectionEvent(NetworkEvent event) {
- if (event instanceof DnsEvent) {
- DnsEvent dnsEvent = (DnsEvent) event;
- return new IntrusionDetectionEvent(dnsEvent);
- } else if (event instanceof ConnectEvent) {
- ConnectEvent connectEvent = (ConnectEvent) event;
- return new IntrusionDetectionEvent(connectEvent);
+ private void incrementEventID() {
+ if (mId == Long.MAX_VALUE) {
+ Slog.i(TAG, "Reached maximum id value; wrapping around.");
+ mId = 0;
+ } else {
+ mId++;
}
- throw new IllegalArgumentException(
- "Invalid event type with ID: "
- + event.getId()
- + "from package: "
- + event.getPackageName());
}
+
+ private final INetdEventCallback mNetdEventCallback =
+ new BaseNetdEventCallback() {
+ @Override
+ public void onDnsEvent(
+ int netId,
+ int eventType,
+ int returnCode,
+ String hostname,
+ String[] ipAddresses,
+ int ipAddressesCount,
+ long timestamp,
+ int uid) {
+ if (!mIsNetworkLoggingEnabled.get()) {
+ return;
+ }
+ DnsEvent dnsEvent =
+ new DnsEvent(
+ hostname,
+ ipAddresses,
+ ipAddressesCount,
+ mPm.getNameForUid(uid),
+ timestamp);
+ dnsEvent.setId(mId);
+ incrementEventID();
+ mDataAggregator.addSingleData(new IntrusionDetectionEvent(dnsEvent));
+ }
+
+ @Override
+ public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
+ if (!mIsNetworkLoggingEnabled.get()) {
+ return;
+ }
+ ConnectEvent connectEvent =
+ new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid), timestamp);
+ connectEvent.setId(mId);
+ incrementEventID();
+ mDataAggregator.addSingleData(new IntrusionDetectionEvent(connectEvent));
+ }
+ };
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
index c5f736e383b2..5611905bf270 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
@@ -43,26 +43,9 @@ public class SecurityLogSource implements DataSource {
mDataAggregator = dataAggregator;
mDpm = context.getSystemService(DevicePolicyManager.class);
mExecutor = Executors.newSingleThreadExecutor();
- }
-
- @Override
- public boolean initialize() {
- // Confirm caller is system and the device is managed. Otherwise logs will
- // be redacted.
- try {
- if (!mDpm.isDeviceManaged()) {
- Slog.e(TAG, "Caller does not have device owner permissions");
- return false;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "Security exception in initialize: ", e);
- return false;
- }
mEventCallback = new SecurityEventCallback();
- return true;
}
-
@Override
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void enable() {
@@ -99,6 +82,10 @@ public class SecurityLogSource implements DataSource {
@Override
public void accept(List<SecurityEvent> events) {
+ if (events.size() == 0) {
+ Slog.w(TAG, "No events received; caller may not be authorized");
+ return;
+ }
List<IntrusionDetectionEvent> intrusionDetectionEvents =
events.stream()
.filter(event -> event != null)
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 908f51b9cba9..f8877ad912b5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -129,6 +129,8 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -339,7 +341,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onDisplayAdded(int displayId) {}
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ mDisplayUiState.put(displayId, new UiState());
+ }
+ }
@Override
public void onDisplayRemoved(int displayId) {
@@ -1710,8 +1716,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
icons = new ArrayMap<>(mIcons);
}
synchronized (mLock) {
- // TODO(b/118592525): Currently, status bar only works on the default display.
- // Make it aware of multi-display if needed.
final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
@@ -1722,6 +1726,46 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
+ @Override
+ public Map<String, RegisterStatusBarResult> registerStatusBarForAllDisplays(IStatusBar bar) {
+ enforceStatusBarService();
+ enforceValidCallingUser();
+
+ Slog.i(TAG, "registerStatusBarForAllDisplays bar=" + bar);
+ mBar = bar;
+ mDeathRecipient.linkToDeath();
+ notifyBarAttachChanged();
+
+ synchronized (mLock) {
+ Map<String, RegisterStatusBarResult> results = new HashMap<>();
+
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ final int displayId = mDisplayUiState.keyAt(i);
+ final UiState state = mDisplayUiState.get(displayId);
+
+ final ArrayMap<String, StatusBarIcon> icons;
+ synchronized (mIcons) {
+ icons = new ArrayMap<>(mIcons);
+ }
+
+ if (state != null) {
+ results.put(String.valueOf(displayId),
+ new RegisterStatusBarResult(icons,
+ gatherDisableActionsLocked(mCurrentUserId, 1),
+ state.mAppearance, state.mAppearanceRegions,
+ state.mImeWindowVis,
+ state.mImeBackDisposition, state.mShowImeSwitcher,
+ gatherDisableActionsLocked(mCurrentUserId, 2),
+ state.mNavbarColorManagedByIme, state.mBehavior,
+ state.mRequestedVisibleTypes,
+ state.mPackageName, state.mTransientBarTypes,
+ state.mLetterboxDetails));
+ }
+ }
+ return results;
+ }
+ }
+
private void notifyBarAttachChanged() {
UiThread.getHandler().post(() -> {
if (mGlobalActionListener == null) return;
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 63a3e5ae7d8b..a38fc5bb6e45 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.PackageManagerInternal;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -63,7 +64,10 @@ public class TelecomLoaderService extends SystemService {
// as this loader (process="system") that's redundant here.
try {
ITelecomLoader telecomLoader = ITelecomLoader.Stub.asInterface(service);
- ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo);
+ PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo,
+ packageManagerInternal.getSystemUiServiceComponent().getPackageName());
SmsApplication.getDefaultMmsApplication(mContext, false);
ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder());
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 3f814f9db1c8..89c7a3d89a54 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -57,6 +57,7 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
@@ -103,6 +104,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -110,6 +112,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.apphibernation.AppHibernationService;
+import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@@ -388,6 +391,14 @@ class ActivityMetricsLogger {
return;
}
if (mLastLaunchedActivity != null) {
+ if (mLastLaunchedActivity.mLaunchCookie != null) {
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "Transferring launch cookie=%s from=%s(%d) to=%s(%d)",
+ mLastLaunchedActivity.mLaunchCookie,
+ mLastLaunchedActivity.packageName,
+ System.identityHashCode(mLastLaunchedActivity), r.packageName,
+ System.identityHashCode(r));
+ }
// Transfer the launch cookie and launch root task because it is a consecutive
// launch event.
r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
@@ -778,11 +789,12 @@ class ActivityMetricsLogger {
*/
private void updateSplitPairLaunches(@NonNull TransitionInfo info) {
final Task launchedActivityTask = info.mLastLaunchedActivity.getTask();
- final Task adjacentToLaunchedTask = launchedActivityTask.getAdjacentTask();
- if (adjacentToLaunchedTask == null) {
+ final Task launchedSplitRootTask = launchedActivityTask.getTaskWithAdjacent();
+ if (launchedSplitRootTask == null) {
// Not a part of a split pair
return;
}
+
for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
final TransitionInfo otherInfo = mTransitionInfoList.get(i);
if (otherInfo == info) {
@@ -790,7 +802,15 @@ class ActivityMetricsLogger {
}
final Task otherTask = otherInfo.mLastLaunchedActivity.getTask();
// The adjacent task is the split root in which activities are started
- if (otherTask.isDescendantOf(adjacentToLaunchedTask)) {
+ final boolean isDescendantOfAdjacent;
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks(
+ otherTask::isDescendantOf);
+ } else {
+ isDescendantOfAdjacent = otherTask.isDescendantOf(
+ launchedSplitRootTask.getAdjacentTask());
+ }
+ if (isDescendantOfAdjacent) {
if (DEBUG_METRICS) {
Slog.i(TAG, "Found adjacent tasks t1=" + launchedActivityTask.mTaskId
+ " t2=" + otherTask.mTaskId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9956d8580686..64758eb5144a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -137,6 +137,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATIO
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SWITCH;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
@@ -248,7 +249,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.sEnableShellTransitions;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
-import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -2138,6 +2138,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
mLaunchCookie = options.getLaunchCookie();
mLaunchRootTask = options.getLaunchRootTask();
+ if (mLaunchCookie != null) {
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "Activity created with launch cookie=%s act=%s(%d)",
+ mLaunchCookie, packageName, System.identityHashCode(this));
+ }
} else {
mHasSceneTransition = false;
}
@@ -2406,9 +2411,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- final TaskSnapshot snapshot =
- mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
- false /* restoreFromDisk */, false /* isLowResolution */);
+ final TaskSnapshot snapshot = mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId,
+ false /* isLowResolution */);
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
@@ -2650,7 +2654,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Skip copy splash screen to client if it was resized, or the starting data already
// requested to be removed after transaction commit.
|| (mStartingData != null && (mStartingData.mResizedFromTransfer
- || mStartingData.mRemoveAfterTransaction != AFTER_TRANSACTION_IDLE))
+ || mStartingData.mRemoveAfterTransaction
+ == AFTER_TRANSACTION_REMOVE_DIRECTLY))
|| isRelaunching()) {
return false;
}
@@ -3212,8 +3217,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* will be ignored.
*/
boolean isUniversalResizeable() {
- final boolean isLargeScreen = mDisplayContent != null && mDisplayContent.getConfiguration()
- .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+ final boolean isLargeScreen = mDisplayContent != null && mDisplayContent.isLargeScreen()
&& mDisplayContent.getIgnoreOrientationRequest();
if (!canBeUniversalResizeable(info.applicationInfo, mWmService, isLargeScreen,
true /* forActivity */)) {
@@ -4143,6 +4147,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
r -> r.mLaunchCookie == null && !r.finishing && r.isUid(getUid()),
this, false /* includeBoundary */, false /* traverseTopToBottom */);
if (nextCookieTarget != null) {
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "Transferring launch cookie=%s on finish from=%s(%d) to=%s(%d)",
+ mLaunchCookie, packageName, System.identityHashCode(this),
+ nextCookieTarget.packageName, System.identityHashCode(nextCookieTarget));
nextCookieTarget.mLaunchCookie = mLaunchCookie;
mLaunchCookie = null;
}
@@ -4585,6 +4593,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /**
+ * Returns {@code true} if the requested orientation of this activity is the same as the
+ * resolved orientation of the from activity.
+ */
+ private boolean isStartingOrientationCompatible(@NonNull ActivityRecord fromActivity) {
+ final int fromOrientation = fromActivity.getConfiguration().orientation;
+ final int requestedOrientation = getRequestedConfigurationOrientation();
+ if (requestedOrientation == ORIENTATION_UNDEFINED) {
+ return fromOrientation == getConfiguration().orientation;
+ }
+ return fromOrientation == requestedOrientation;
+ }
+
private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
final WindowState tStartingWindow = fromActivity.mStartingWindow;
if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
@@ -4604,13 +4625,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Do not transfer if the orientation doesn't match, redraw starting window while it is
// on top will cause flicker.
- final int fromOrientation = fromActivity.getConfiguration().orientation;
- final int requestedOrientation = getRequestedConfigurationOrientation();
- if (requestedOrientation == ORIENTATION_UNDEFINED) {
- if (fromOrientation != getConfiguration().orientation) {
- return false;
- }
- } else if (fromOrientation != requestedOrientation) {
+ if (!isStartingOrientationCompatible(fromActivity)) {
return false;
}
@@ -4708,6 +4723,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
return true;
} else if (fromActivity.mStartingData != null) {
+ if (fromActivity.mStartingData instanceof SnapshotStartingData
+ && !isStartingOrientationCompatible(fromActivity)) {
+ // Do not transfer because the snapshot will be distorted in different orientation.
+ return false;
+ }
// The previous app was getting ready to show a
// starting window, but hasn't yet done so. Steal it!
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
@@ -5408,10 +5428,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void setDeferHidingClient() {
+ if (Flags.removeDeferHidingClient()) {
+ return;
+ }
mDeferHidingClient = true;
}
void clearDeferHidingClient() {
+ if (Flags.removeDeferHidingClient()) {
+ return;
+ }
if (!mDeferHidingClient) return;
mDeferHidingClient = false;
if (!mVisibleRequested) {
@@ -5590,18 +5616,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// stopped, then we need to set up to wait for its windows to be ready.
if (!isVisible() || mAppStopped) {
clearAllDrawn();
- // Reset the draw state in order to prevent the starting window to be immediately
- // dismissed when the app still has the surface.
- if (!Flags.resetDrawStateOnClientInvisible()
- && !isVisible() && !isClientVisible()) {
- forAllWindows(w -> {
- if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
- w.mWinAnimator.resetDrawState();
- // Force add to mResizingWindows, so the window will report drawn.
- w.forceReportingResized();
- }
- }, true /* traverseTopToBottom */);
- }
}
// In the case where we are making an app visible but holding off for a transition,
@@ -7141,9 +7155,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void setClientVisible(boolean clientVisible) {
- // TODO(shell-transitions): Remove mDeferHidingClient once everything is shell-transitions.
- // pip activities should just remain in clientVisible.
- if (!clientVisible && mDeferHidingClient) return;
+ if (!Flags.removeDeferHidingClient()) {
+ // TODO(shell-transitions): Remove mDeferHidingClient once everything is
+ // shell-transitions. pip activities should just remain in clientVisible.
+ if (!clientVisible && mDeferHidingClient) return;
+ }
super.setClientVisible(clientVisible);
}
@@ -8491,7 +8507,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
&& (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
|| parentWindowingMode == WINDOWING_MODE_FULLSCREEN
- || AppCompatCameraPolicy.shouldCameraCompatControlOrientation(this)
+ || AppCompatCameraPolicy.isFreeformLetterboxingForCameraAllowed(this)
// When starting to switch between PiP and fullscreen, the task is pinned
// and the activity is fullscreen. But only allow to apply letterbox if the
// activity is exiting PiP because an entered PiP should fill the task.
@@ -10332,7 +10348,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
.isVisibilityUnknown(this)) {
return false;
}
- if (!isVisibleRequested()) return true;
+ if (!isVisibleRequested()) {
+ // TODO(b/294925498): Remove this finishing check once we have accurate ready tracking.
+ if (task != null && task.getPausingActivity() == this) {
+ // Visibility of starting activities isn't calculated until pause-complete, so if
+ // this is not paused yet, don't consider it ready.
+ return false;
+ }
+ return true;
+ }
if (mPendingRelaunchCount > 0) return false;
// Wait for attach. That is the earliest time where we know if there will be an associated
// display rotation. If we don't wait, the starting-window can finishDrawing first and
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 1208b6ef396f..08ceb61e14a8 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -142,6 +142,8 @@ public class ActivityServiceConnectionsHolder<T> {
/** Used by {@link ActivityRecord#dump}. */
@Override
public String toString() {
- return String.valueOf(mConnections);
+ synchronized (mActivity) {
+ return String.valueOf(mConnections);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 24ed1bbe0eb1..9aaa0e1cfd6b 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -18,6 +18,8 @@ package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -147,7 +149,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
for (int i = activities.length - 1; i >= 0; --i) {
fileId ^= getSystemHashCode(activities[i]);
}
- return tmpUsf.mFileId == fileId ? mCache.getSnapshot(tmpUsf.mActivityIds.get(0)) : null;
+ return tmpUsf.mFileId == fileId
+ ? mCache.getSnapshotInner(tmpUsf.mActivityIds.get(0)) : null;
}
private void cleanUpUserFiles(int userId) {
@@ -343,6 +346,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
if (DEBUG) {
Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
}
+ if (mPersister.mSnapshotPersistQueue.peekWriteQueueSize() >= MAX_STORE_QUEUE_DEPTH
+ || mPersister.mSnapshotPersistQueue.peekQueueSize() > MAX_PERSIST_SNAPSHOT_COUNT) {
+ Slog.w(TAG, "Skipping recording activity snapshot, too many requests!");
+ return;
+ }
final int size = activity.size();
final int[] mixedCode = new int[size];
if (size == 1) {
@@ -432,7 +440,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot");
} else {
// remove the snapshot for the one below close
- addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot");
+ addBelowActivityIfExist(ar, mPendingRemoveActivity, false, "remove-snapshot");
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 90d3834b4543..acb93844c945 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -27,6 +27,7 @@ import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.isStartResultSuccessful;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
@@ -64,6 +65,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -891,7 +893,10 @@ class ActivityStarter {
final ActivityOptions originalOptions = mRequest.activityOptions != null
? mRequest.activityOptions.getOriginalOptions() : null;
// Only track the launch time of activity that will be resumed.
- launchingRecord = mDoResume ? mLastStartActivityRecord : null;
+ if (mDoResume || (isStartResultSuccessful(res)
+ && mLastStartActivityRecord.getTask().isVisibleRequested())) {
+ launchingRecord = mLastStartActivityRecord;
+ }
// If the new record is the one that started, a new activity has created.
final boolean newActivityCreated = mStartActivity == launchingRecord;
// Notify ActivityMetricsLogger that the activity has launched.
@@ -3093,6 +3098,10 @@ class ActivityStarter {
// options if set.
if (mStartActivity.mLaunchCookie != null) {
intentActivity.mLaunchCookie = mStartActivity.mLaunchCookie;
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "Updating launch cookie=%s act=%s(%d)",
+ intentActivity.mLaunchCookie, intentActivity.packageName,
+ System.identityHashCode(intentActivity));
}
if (mStartActivity.mPendingRemoteAnimation != null) {
intentActivity.mPendingRemoteAnimation = mStartActivity.mPendingRemoteAnimation;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8ff08187c698..5eee8ece6a67 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1555,7 +1555,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityRecord[] outActivity = new ActivityRecord[1];
- getActivityStartController().obtainStarter(intent, "dream")
+ final int res = getActivityStartController()
+ .obtainStarter(intent, "dream")
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setCallingPackage(intent.getPackage())
@@ -1569,9 +1570,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.execute();
final ActivityRecord started = outActivity[0];
- final IAppTask appTask = started == null ? null :
- new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
- return appTask;
+ if (started == null || !ActivityManager.isStartResultSuccessful(res)) {
+ // start the dream activity failed.
+ return null;
+ }
+ return new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
}
}
@@ -3794,6 +3797,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r.setPictureInPictureParams(params);
enterPipTransition.setPipActivity(r);
r.mAutoEnteringPip = isAutoEnter;
+
+ if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
+ && enterPipTransition != null) {
+ enterPipTransition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+ }
+
getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
getTransitionController().requestStartTransition(enterPipTransition,
r.getTask(), null /* remoteTransition */, null /* displayChange */);
@@ -3987,15 +3996,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
// Try to load snapshot from cache first, and add reference if the snapshot is in cache.
final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
- task.mUserId, false /* restoreFromDisk */, isLowResolution);
+ isLowResolution, usage);
if (snapshot != null) {
- snapshot.addReference(usage);
return snapshot;
}
}
// Don't call this while holding the lock as this operation might hit the disk.
- return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
- task.mUserId, true /* restoreFromDisk */, isLowResolution);
+ return mWindowManager.mTaskSnapshotController.getSnapshotFromDisk(taskId,
+ task.mUserId, isLowResolution, usage);
}
@Override
@@ -4011,10 +4019,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
}
+ final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(
+ taskId, isLowResolution, TaskSnapshot.REFERENCE_WRITE_TO_PARCEL);
+ if (snapshot != null) {
+ return snapshot;
+ }
}
// Don't call this while holding the lock as this operation might hit the disk.
- return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
- task.mUserId, true /* restoreFromDisk */, isLowResolution);
+ return mWindowManager.mTaskSnapshotController.getSnapshotFromDisk(taskId,
+ task.mUserId, isLowResolution, TaskSnapshot.REFERENCE_WRITE_TO_PARCEL);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index a077a0b9a2ca..0aff1de72cb1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -54,6 +54,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
@@ -1268,7 +1269,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Checks if the caller can be shown in the given public display.
int userId = UserHandle.getUserId(callingUid);
int displayId = display.getDisplayId();
- boolean allowed = mWindowManager.mUmInternal.isUserVisible(userId, displayId);
+ boolean allowed = userId == UserHandle.USER_SYSTEM
+ || mWindowManager.mUmInternal.isUserVisible(userId, displayId);
ProtoLog.d(WM_DEBUG_TASKS,
"Launch on display check: %s launch for userId=%d on displayId=%d",
(allowed ? "allow" : "disallow"), userId, displayId);
@@ -2842,6 +2844,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
targetActivity.applyOptionsAnimation();
if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "Updating launch cookie=%s for start from recents act=%s(%d)",
+ targetActivity.mLaunchCookie, targetActivity.packageName,
+ System.identityHashCode(targetActivity));
}
} finally {
mActivityMetricsLogger.notifyActivityLaunched(launchingState,
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 8be66ccfbd70..9547e5cc8ba9 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -193,6 +193,17 @@ class AppCompatCameraPolicy {
}
// TODO(b/369070416): have policies implement the same interface.
+ static boolean isFreeformLetterboxingForCameraAllowed(@NonNull ActivityRecord activity) {
+ final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
+ if (cameraPolicy == null) {
+ return false;
+ }
+ return cameraPolicy.mCameraCompatFreeformPolicy != null
+ && cameraPolicy.mCameraCompatFreeformPolicy
+ .isFreeformLetterboxingForCameraAllowed(activity);
+ }
+
+ // TODO(b/369070416): have policies implement the same interface.
static boolean shouldCameraCompatControlAspectRatio(@NonNull ActivityRecord activity) {
final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
if (cameraPolicy == null) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 741eefae4462..492d84f4a12f 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -996,8 +996,7 @@ public class AppTransitionController {
// If the current window container is a task with adjacent task set, the both
// adjacent tasks will be opened or closed together. To get their opening or
// closing animation target independently, skip promoting their animation targets.
- if (current.asTask() != null
- && current.asTask().getAdjacentTask() != null) {
+ if (current.asTask() != null && current.asTask().hasAdjacentTask()) {
canPromote = false;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 3968b525f11d..1a7c6b70f007 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -275,12 +275,8 @@ class BackNavigationController {
final boolean isOccluded = isKeyguardOccluded(window);
if (!canAnimate) {
backType = BackNavigationInfo.TYPE_CALLBACK;
- } else if ((window.getParent().getChildCount() > 1
- && window.getParent().getChildAt(0) != window)) {
- // TODO Dialog window does not need to attach on activity, check
- // window.mAttrs.type != TYPE_BASE_APPLICATION
- // Are we the top window of our parent? If not, we are a window on top of the
- // activity, we won't close the activity.
+ } else if (window.mAttrs.type != TYPE_BASE_APPLICATION) {
+ // The focus window belongs to an activity and it's not the base window.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
removedWindowContainer = window;
} else if (hasTranslucentActivity(currentActivity, prevActivities)) {
@@ -2251,8 +2247,7 @@ class BackNavigationController {
if (w.asTask() != null) {
final Task task = w.asTask();
snapshot = task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
- task.mTaskId, task.mUserId, false /* restoreFromDisk */,
- false /* isLowResolution */);
+ task.mTaskId, false /* isLowResolution */);
} else {
ActivityRecord ar = w.asActivityRecord();
if (ar == null && w.asTaskFragment() != null) {
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 1676bfadab43..1b9454ec2272 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -38,7 +38,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.CameraCompatTaskInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.os.RemoteException;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -180,12 +182,39 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
if (activity != null) {
activity.recomputeConfiguration();
mCameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
+ updateCompatibilityInfo(activity);
activity.ensureActivityConfiguration(/* ignoreVisibility= */ true);
} else {
mCameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
}
}
+ private void updateCompatibilityInfo(@NonNull ActivityRecord activityRecord) {
+ final CompatibilityInfo compatibilityInfo = activityRecord.mAtmService
+ .compatibilityInfoForPackageLocked(activityRecord.info.applicationInfo);
+ compatibilityInfo.applicationDisplayRotation =
+ CameraCompatTaskInfo.getDisplayRotationFromCameraCompatMode(
+ getCameraCompatMode(activityRecord));
+ try {
+ // TODO(b/380840084): Consider using a ClientTransaction for this update.
+ activityRecord.app.getThread().updatePackageCompatibilityInfo(
+ activityRecord.packageName, compatibilityInfo);
+ } catch (RemoteException e) {
+ ProtoLog.w(WmProtoLogGroups.WM_DEBUG_STATES,
+ "Unable to update CompatibilityInfo for app %s", activityRecord.app);
+ }
+ }
+
+ /**
+ * Returns true if letterboxing should be allowed for camera apps, even if otherwise it isn't.
+ *
+ * <p>Camera compat is currently the only use-case of letterboxing for desktop windowing.
+ */
+ boolean isFreeformLetterboxingForCameraAllowed(@NonNull ActivityRecord activity) {
+ // Letterboxing is normally not allowed in desktop windowing.
+ return isCameraRunningAndWindowingModeEligible(activity);
+ }
+
boolean shouldCameraCompatControlOrientation(@NonNull ActivityRecord activity) {
return isCameraRunningAndWindowingModeEligible(activity);
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 93ccd74c6b23..8eccffd8fe3b 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -243,6 +243,19 @@ final class ContentRecorder implements WindowContainerListener {
}
}
+ /** Called when the surface of display is changed to a different instance. */
+ void resetRecordingDisplay(int displayId) {
+ if (!isCurrentlyRecording()
+ || mContentRecordingSession.getDisplayToRecord() != displayId) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Display %d changed surface so stop recording", displayId);
+ mDisplayContent.mWmService.mTransactionFactory.get().remove(mRecordedSurface).apply();
+ mRecordedSurface = null;
+ // Do not un-set the token, in case new surface is ready and recording should begin again.
+ }
+
/**
* Pauses recording on this display content. Note the session does not need to be updated,
* since recording can be resumed still.
@@ -460,15 +473,17 @@ final class ContentRecorder implements WindowContainerListener {
case RECORD_CONTENT_TASK:
// Given the WindowToken of the region to record, retrieve the associated
// SurfaceControl.
- if (tokenToRecord == null) {
+ final WindowContainer wc = tokenToRecord != null
+ ? WindowContainer.fromBinder(tokenToRecord) : null;
+ if (wc == null) {
handleStartRecordingFailed();
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to start recording due to null token for "
- + "display %d",
+ "Content Recording: Unable to start recording due to null token or " +
+ "null window container for " + "display %d",
mDisplayContent.getDisplayId());
return null;
}
- Task taskToRecord = WindowContainer.fromBinder(tokenToRecord).asTask();
+ final Task taskToRecord = wc.asTask();
if (taskToRecord == null) {
handleStartRecordingFailed();
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 37e8f6260420..4eaa11bac016 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -193,7 +193,7 @@ class DeferredDisplayUpdater {
final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
mDisplayContent.mInitialDisplayHeight);
final int fromRotation = mDisplayContent.getRotation();
- if (Flags.blastSyncNotificationShadeOnDisplaySwitch() && physicalDisplayUpdated) {
+ if (physicalDisplayUpdated) {
final WindowState notificationShade =
mDisplayContent.getDisplayPolicy().getNotificationShade();
if (notificationShade != null && notificationShade.isVisible()
@@ -432,7 +432,8 @@ class DeferredDisplayUpdater {
|| !first.thermalRefreshRateThrottling.contentEquals(
second.thermalRefreshRateThrottling)
|| !Objects.equals(first.thermalBrightnessThrottlingDataId,
- second.thermalBrightnessThrottlingDataId)) {
+ second.thermalBrightnessThrottlingDataId)
+ || first.canHostTasks != second.canHostTasks) {
diff |= DIFF_NOT_WM_DEFERRABLE;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e19096354d64..f8086615b7d1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -260,6 +260,7 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RegionUtils;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -438,6 +439,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mSandboxDisplayApis = true;
+ /** Whether {@link #setIgnoreOrientationRequest} is called to override the default policy. */
+ @VisibleForTesting
+ boolean mHasSetIgnoreOrientationRequest;
+
/**
* Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
* but can be set from Settings or via shell command "adb shell wm density".
@@ -1272,7 +1277,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
void migrateToNewSurfaceControl(Transaction t) {
t.remove(mSurfaceControl);
-
+ // Reset the recording displays which were mirroring this display.
+ for (int i = mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+ final ContentRecorder recorder = mRootWindowContainer.getChildAt(i).mContentRecorder;
+ if (recorder != null) {
+ recorder.resetRecordingDisplay(mDisplayId);
+ }
+ }
mLastSurfacePosition.set(0, 0);
mLastDeltaRotation = Surface.ROTATION_0;
@@ -1384,11 +1395,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTokenMap.put(binder, token);
if (token.asActivityRecord() == null) {
- // Set displayContent for non-app token to prevent same token will add twice after
- // onDisplayChanged.
- // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
- // (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
- token.mDisplayContent = this;
+ // Setting the mDisplayContent to the token is not needed: it is done by da.addChild
+ // below, that also calls onDisplayChanged once moved.
+ if (!Flags.reparentWindowTokenApi()) {
+ // Set displayContent for non-app token to prevent same token will add twice after
+ // onDisplayChanged.
+ // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
+ // (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent
+ // assigned.
+ token.mDisplayContent = this;
+ }
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
@@ -2274,7 +2290,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (shellTransitions) {
// Before setDisplayProjection is applied by the start transaction of transition,
// set the transform hint to avoid using surface in old rotation.
- getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation);
+ setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
// The sync transaction should already contains setDisplayProjection, so unset the
// hint to restore the natural state when the transaction is applied.
transaction.unsetFixedTransformHint(mSurfaceControl);
@@ -2284,6 +2300,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
}
+ void setFixedTransformHint(Transaction t, SurfaceControl sc, int rotation) {
+ t.setFixedTransformHint(sc, (rotation + mDisplayInfo.installOrientation) % 4);
+ }
+
void configureDisplayPolicy() {
mRootWindowContainer.updateDisplayImePolicyCache();
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
@@ -6706,8 +6726,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mDisplayPolicy.getSystemUiContext();
}
+ /** Returns {@code} true if the smallest screen width dp >= 600. */
+ boolean isLargeScreen() {
+ return getConfiguration().smallestScreenWidthDp
+ >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
+ }
+
+ @Override
+ boolean getIgnoreOrientationRequest() {
+ if (mHasSetIgnoreOrientationRequest
+ || !com.android.window.flags.Flags.universalResizableByDefault()) {
+ return super.getIgnoreOrientationRequest();
+ }
+ // Large screen (sw >= 600dp) ignores orientation request by default.
+ return isLargeScreen() && !mWmService.isIgnoreOrientationRequestDisabled();
+ }
+
@Override
boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ mHasSetIgnoreOrientationRequest = true;
if (mSetIgnoreOrientationRequest == ignoreOrientationRequest) return false;
final boolean rotationChanged = super.setIgnoreOrientationRequest(ignoreOrientationRequest);
mWmService.mDisplayWindowSettings.setIgnoreOrientationRequest(
@@ -7109,9 +7146,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/**
* @see #getRequestedVisibleTypes()
*/
- void setRequestedVisibleTypes(@InsetsType int requestedVisibleTypes) {
- if (mRequestedVisibleTypes != requestedVisibleTypes) {
- mRequestedVisibleTypes = requestedVisibleTypes;
+ void updateRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
+ int newRequestedVisibleTypes =
+ (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
+ if (mRequestedVisibleTypes != newRequestedVisibleTypes) {
+ mRequestedVisibleTypes = newRequestedVisibleTypes;
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 659bb6784c89..01e00e9d67f7 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2485,7 +2485,7 @@ public class DisplayPolicy {
final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final boolean adjacentTasksVisible =
defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
- && task.getTopLeafTask().getAdjacentTask() != null)
+ && task.getTopLeafTask().hasAdjacentTask())
!= null;
final Task topFreeformTask = defaultTaskDisplayArea
.getTopRootTaskInWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f6d05d08cb04..f0ba822c37c5 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -374,9 +374,9 @@ class DisplayWindowSettings {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
- final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
- ? settings.mIgnoreOrientationRequest : false;
- dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
+ if (settings.mIgnoreOrientationRequest != null) {
+ dc.setIgnoreOrientationRequest(settings.mIgnoreOrientationRequest);
+ }
dc.getDisplayRotation().resetAllowAllRotations();
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 2a5a3a5638d2..1c4e487d2e7e 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -528,7 +528,7 @@ class DragState {
}
// Only allow the extras to be dispatched to a global-intercepting drag target
ClipData data = null;
- if (interceptsGlobalDrag) {
+ if (interceptsGlobalDrag && mData != null) {
data = mData.copyForTransferWithActivityInfo();
PersistableBundle extras = data.getDescription().getExtras() != null
? data.getDescription().getExtras()
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 4230cd868c03..cba606cf2b0c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -324,7 +324,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (target != imeControlTarget) {
// TODO(b/353463205): check if fromUser=false is correct here
boolean imeVisible = target.isRequestedVisible(WindowInsets.Type.ime());
- ImeTracker.Token statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.Token statsToken = ImeTracker.forLogging().onStart(
+ imeVisible ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
ImeTracker.ORIGIN_SERVER,
imeVisible ? SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED
: SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 9d21183c6c03..7751ac3f9fc6 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -128,8 +128,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
@Override
public void notifyConfigurationChanged() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "notifyConfigurationChanged");
- final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange()
- || updateLastInputConfigurationSources();
+ final boolean changed = updateLastInputConfigurationSources();
// Even if the input devices are not changed, there could be other pending changes
// during booting. It's fine to apply earlier.
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 24a6f118ad04..4bcba13448e9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -258,16 +258,13 @@ class InsetsPolicy {
* We also need to exclude certain types of insets source for client within specific windowing
* modes.
*
- * @param attrs the LayoutParams of the target
- * @param windowingMode the windowing mode of the target
- * @param isAlwaysOnTop is the target always on top
+ * @param target the target on which the policy is applied
* @param state the input inset state containing all the sources
* @return The state stripped of the necessary information.
*/
- InsetsState enforceInsetsPolicyForTarget(WindowManager.LayoutParams attrs,
- @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
- InsetsState state) {
+ InsetsState enforceInsetsPolicyForTarget(WindowState target, InsetsState state) {
final InsetsState originalState = state;
+ final WindowManager.LayoutParams attrs = target.getAttrs();
// The caller should not receive the visible insets provided by itself.
if (attrs.type == TYPE_INPUT_METHOD) {
@@ -316,12 +313,17 @@ class InsetsPolicy {
}
}
+ final @WindowConfiguration.WindowingMode int windowingMode = target.getWindowingMode();
if (WindowConfiguration.isFloating(windowingMode)
- || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
+ || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && target.isAlwaysOnTop())) {
// Keep frames, caption, and IME.
int types = WindowInsets.Type.captionBar();
if (windowingMode != WINDOWING_MODE_PINNED) {
- types |= WindowInsets.Type.ime();
+ if (!Flags.refactorInsetsController() || (mDisplayContent != null
+ && target == mDisplayContent.getImeInputTarget()
+ && (WindowInsets.Type.ime() & target.getRequestedVisibleTypes()) != 0)) {
+ types |= WindowInsets.Type.ime();
+ }
}
final InsetsState newState = new InsetsState();
newState.set(state, types);
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 27f82d90fdac..7fdc2c67b5ce 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -40,7 +40,7 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
+import static com.android.launcher3.Flags.enableUseTopVisibleActivityForExcludeFromRecentTask;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
@@ -1529,7 +1529,7 @@ class RecentTasks {
// The Recents is only supported on default display now, we should only keep the
// most recent task of home display.
boolean isMostRecentTask;
- if (enableRefactorTaskThumbnail()) {
+ if (enableUseTopVisibleActivityForExcludeFromRecentTask()) {
isMostRecentTask = task.getTopVisibleActivity() != null;
} else {
isMostRecentTask = taskIndex == 0;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 46312aff1fb6..3d2868540334 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -156,6 +156,7 @@ import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
import com.android.server.wm.utils.RegionUtils;
+import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -262,6 +263,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
int mCurrentUser;
/** Root task id of the front root task when user switched, indexed by userId. */
SparseIntArray mUserRootTaskInFront = new SparseIntArray(2);
+ SparseArray<IntArray> mUserVisibleRootTasks = new SparseArray<>();
/**
* A list of tokens that cause the top activity to be put to sleep.
@@ -1794,12 +1796,24 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
activityAssistInfos.clear();
activityAssistInfos.add(new ActivityAssistInfo(top));
// Check if the activity on the split screen.
- final Task adjacentTask = top.getTask().getAdjacentTask();
- if (adjacentTask != null) {
- final ActivityRecord adjacentActivityRecord =
- adjacentTask.getTopNonFinishingActivity();
- if (adjacentActivityRecord != null) {
- activityAssistInfos.add(new ActivityAssistInfo(adjacentActivityRecord));
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ top.getTask().forOtherAdjacentTasks(task -> {
+ final ActivityRecord adjacentActivityRecord =
+ task.getTopNonFinishingActivity();
+ if (adjacentActivityRecord != null) {
+ activityAssistInfos.add(
+ new ActivityAssistInfo(adjacentActivityRecord));
+ }
+ });
+ } else {
+ final Task adjacentTask = top.getTask().getAdjacentTask();
+ if (adjacentTask != null) {
+ final ActivityRecord adjacentActivityRecord =
+ adjacentTask.getTopNonFinishingActivity();
+ if (adjacentActivityRecord != null) {
+ activityAssistInfos.add(
+ new ActivityAssistInfo(adjacentActivityRecord));
+ }
}
}
if (rootTask == topFocusedRootTask) {
@@ -1924,7 +1938,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// appropriate.
removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
- mUserRootTaskInFront.put(mCurrentUser, focusRootTaskId);
+ if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+ final IntArray visibleRootTasks = new IntArray();
+ forAllRootTasks(rootTask -> {
+ if (mCurrentUser == rootTask.mUserId && rootTask.isVisibleRequested()) {
+ visibleRootTasks.add(rootTask.getRootTaskId());
+ }
+ }, /* traverseTopToBottom */ false);
+ mUserVisibleRootTasks.put(mCurrentUser, visibleRootTasks);
+ } else {
+ mUserRootTaskInFront.put(mCurrentUser, focusRootTaskId);
+ }
+
mCurrentUser = userId;
mTaskSupervisor.mStartingUsers.add(uss);
@@ -1937,22 +1962,60 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.i(TAG, "Persisting top task because it belongs to an always-visible user");
// For a normal user-switch, we will restore the new user's task. But if the pre-switch
// top task is an always-visible (Communal) one, keep it even after the switch.
- mUserRootTaskInFront.put(mCurrentUser, focusRootTaskId);
+ if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+ final IntArray rootTasks = mUserVisibleRootTasks.get(mCurrentUser);
+ rootTasks.add(focusRootTaskId);
+ mUserVisibleRootTasks.put(mCurrentUser, rootTasks);
+ } else {
+ mUserRootTaskInFront.put(mCurrentUser, focusRootTaskId);
+ }
+
}
final int restoreRootTaskId = mUserRootTaskInFront.get(userId);
+ final IntArray rootTaskIdsToRestore = mUserVisibleRootTasks.get(userId);
+ boolean homeInFront = false;
+ if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+ if (rootTaskIdsToRestore == null) {
+ // If there are no root tasks saved, try restore id 0 which should create and launch
+ // the home task.
+ handleRootTaskLaunchOnUserSwitch(/* restoreRootTaskId */INVALID_TASK_ID);
+ homeInFront = true;
+ } else {
+ for (int i = 0; i < rootTaskIdsToRestore.size(); i++) {
+ handleRootTaskLaunchOnUserSwitch(rootTaskIdsToRestore.get(i));
+ }
+ // Check if the top task is type home
+ if (rootTaskIdsToRestore.size() > 0) {
+ final int topRootTaskId = rootTaskIdsToRestore.get(
+ rootTaskIdsToRestore.size() - 1);
+ homeInFront = isHomeTask(topRootTaskId);
+ }
+ }
+ } else {
+ handleRootTaskLaunchOnUserSwitch(restoreRootTaskId);
+ // Check if the top task is type home
+ homeInFront = isHomeTask(restoreRootTaskId);
+ }
+ return homeInFront;
+ }
+
+ private boolean isHomeTask(int taskId) {
+ final Task rootTask = getRootTask(taskId);
+ return rootTask != null && rootTask.isActivityTypeHome();
+ }
+
+ private void handleRootTaskLaunchOnUserSwitch(int restoreRootTaskId) {
Task rootTask = getRootTask(restoreRootTaskId);
if (rootTask == null) {
rootTask = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
}
- final boolean homeInFront = rootTask.isActivityTypeHome();
if (rootTask.isOnHomeDisplay()) {
rootTask.moveToFront("switchUserOnHomeDisplay");
} else {
// Root task was moved to another display while user was swapped out.
resumeHomeActivity(null, "switchUserOnOtherDisplay", getDefaultTaskDisplayArea());
}
- return homeInFront;
}
/** Returns whether the given user is to be always-visible (e.g. a communal profile). */
@@ -1963,7 +2026,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void removeUser(int userId) {
- mUserRootTaskInFront.delete(userId);
+ if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+ mUserVisibleRootTasks.delete(userId);
+ } else {
+ mUserRootTaskInFront.delete(userId);
+ }
}
/**
@@ -1976,7 +2043,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
rootTask = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
}
- mUserRootTaskInFront.put(userId, rootTask.getRootTaskId());
+ if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+ final IntArray rootTasks = mUserVisibleRootTasks.get(userId, new IntArray());
+ rootTasks.add(rootTask.getRootTaskId());
+ mUserVisibleRootTasks.put(userId, rootTasks);
+ } else {
+ mUserRootTaskInFront.put(userId, rootTask.getRootTaskId());
+ }
}
}
@@ -2124,7 +2197,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (!tf.isOrganizedTaskFragment()) {
return;
}
- tf.resetAdjacentTaskFragment();
+ tf.clearAdjacentTaskFragments();
tf.setCompanionTaskFragment(null /* companionTaskFragment */);
tf.setAnimationParams(TaskFragmentAnimationParams.DEFAULT);
if (tf.getTopNonFinishingActivity() != null) {
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index c20b85858c44..8f0f6860ceb6 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -56,7 +56,7 @@ public class SeamlessRotator {
mOldRotation = oldRotation;
mNewRotation = newRotation;
mApplyFixedTransformHint = applyFixedTransformationHint;
- mFixedTransformHint = oldRotation;
+ mFixedTransformHint = (oldRotation + info.installOrientation) % 4;
final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
final int pH = flipped ? info.logicalWidth : info.logicalHeight;
final int pW = flipped ? info.logicalHeight : info.logicalWidth;
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 1e6ee7dc318f..9812a88bfa5a 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -51,7 +51,7 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
}
@Nullable
- final TaskSnapshot getSnapshot(Integer id) {
+ final TaskSnapshot getSnapshotInner(Integer id) {
synchronized (mLock) {
// Try the running cache.
final CacheEntry entry = mRunningCache.get(id);
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 3ee2e6048634..dcdffa416e33 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -202,7 +202,7 @@ class SnapshotController {
final Task task = wc.asTask();
if (task != null && wc.isVisibleRequested() && !task.inPinnedWindowingMode()) {
final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(task.mTaskId,
- task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */);
+ false /* isLowResolution */);
if (snapshot != null) {
mTaskSnapshotController.removeAndDeleteSnapshot(task.mTaskId, task.mUserId);
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 8b63ecf7135f..a5454546341b 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -50,7 +50,7 @@ import java.util.ArrayDeque;
class SnapshotPersistQueue {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final long DELAY_MS = 100;
- private static final int MAX_STORE_QUEUE_DEPTH = 2;
+ static final int MAX_STORE_QUEUE_DEPTH = 2;
private static final int COMPRESS_QUALITY = 95;
@GuardedBy("mLock")
@@ -154,7 +154,12 @@ class SnapshotPersistQueue {
}
}
- @VisibleForTesting
+ int peekWriteQueueSize() {
+ synchronized (mLock) {
+ return mStoreQueueItems.size();
+ }
+ }
+
int peekQueueSize() {
synchronized (mLock) {
return mWriteQueue.size();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f090ef1b72e9..76d8861022bb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2500,20 +2500,79 @@ class Task extends TaskFragment {
return parentTask == null ? null : parentTask.getCreatedByOrganizerTask();
}
- /** @return the first adjacent task of this task or its parent. */
+ /** @deprecated b/373709676 replace with {@link #forOtherAdjacentTasks(Consumer)} ()}. */
+ @Deprecated
@Nullable
Task getAdjacentTask() {
- final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment();
- if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) {
- return adjacentTaskFragment.asTask();
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("allowMultipleAdjacentTaskFragments is enabled. "
+ + "Use #forOtherAdjacentTasks instead");
+ }
+ final Task taskWithAdjacent = getTaskWithAdjacent();
+ if (taskWithAdjacent == null) {
+ return null;
}
+ return taskWithAdjacent.getAdjacentTaskFragment().asTask();
+ }
+ /** Finds the first Task parent (or itself) that has adjacent. */
+ @Nullable
+ Task getTaskWithAdjacent() {
+ if (hasAdjacentTaskFragment()) {
+ return this;
+ }
final WindowContainer parent = getParent();
if (parent == null || parent.asTask() == null) {
return null;
}
+ return parent.asTask().getTaskWithAdjacent();
+ }
- return parent.asTask().getAdjacentTask();
+ /** Returns true if this or its parent has adjacent Task. */
+ boolean hasAdjacentTask() {
+ return getTaskWithAdjacent() != null;
+ }
+
+ /**
+ * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent
+ * Tasks. The invoke order is not guaranteed.
+ */
+ void forOtherAdjacentTasks(@NonNull Consumer<Task> callback) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. "
+ + "Use #getAdjacentTask instead");
+ }
+
+ final Task taskWithAdjacent = getTaskWithAdjacent();
+ if (taskWithAdjacent == null) {
+ return;
+ }
+ final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments();
+ adjacentTasks.forAllTaskFragments(tf -> {
+ // We don't support Task adjacent to non-Task TaskFragment.
+ callback.accept(tf.asTask());
+ }, taskWithAdjacent /* exclude */);
+ }
+
+ /**
+ * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent
+ * Tasks. Returns early if callback returns true on any of them. The invoke order is not
+ * guaranteed.
+ */
+ boolean forOtherAdjacentTasks(@NonNull Predicate<Task> callback) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. "
+ + "Use getAdjacentTask instead");
+ }
+ final Task taskWithAdjacent = getTaskWithAdjacent();
+ if (taskWithAdjacent == null) {
+ return false;
+ }
+ final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments();
+ return adjacentTasks.forAllTaskFragments(tf -> {
+ // We don't support Task adjacent to non-Task TaskFragment.
+ return callback.test(tf.asTask());
+ }, taskWithAdjacent /* exclude */);
}
// TODO(task-merge): Figure out what's the right thing to do for places that used it.
@@ -2907,7 +2966,7 @@ class Task extends TaskFragment {
Rect outSurfaceInsets) {
// If this task has its adjacent task, it means they should animate together. Use display
// bounds for them could move same as full screen task.
- if (getAdjacentTask() != null) {
+ if (hasAdjacentTask()) {
super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
return;
}
@@ -3425,6 +3484,7 @@ class Task extends TaskFragment {
info.isTopActivityNoDisplay = top != null && top.isNoDisplay();
info.isSleeping = shouldSleepActivities();
info.isTopActivityTransparent = top != null && !top.fillsParent();
+ info.isActivityStackTransparent = !topTask.forAllActivities(r -> (r.occludesParent()));
info.lastNonFullscreenBounds = topTask.mLastNonFullscreenBounds;
final WindowState windowState = top != null
? top.findMainWindow(/* includeStartingApp= */ false) : null;
@@ -3863,6 +3923,9 @@ class Task extends TaskFragment {
if (mLaunchAdjacentDisabled) {
pw.println(prefix + "mLaunchAdjacentDisabled=true");
}
+ if (mReparentLeafTaskIfRelaunch) {
+ pw.println(prefix + "mReparentLeafTaskIfRelaunch=true");
+ }
}
@Override
@@ -4488,7 +4551,7 @@ class Task extends TaskFragment {
}
void onPictureInPictureParamsChanged() {
- if (inPinnedWindowingMode()) {
+ if (inPinnedWindowingMode() || Flags.enableDesktopWindowingPip()) {
dispatchTaskInfoChangedIfNeeded(true /* force */);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 2c71c1a1f4f3..9564c5959d98 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -31,6 +31,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
@@ -60,6 +61,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.LaunchParamsController.LaunchParams;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -918,6 +920,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
if (candidateTask.getParent() == null) {
addChild(candidateTask, position);
} else {
+ if (candidateTask.getRootTask().mReparentLeafTaskIfRelaunch) {
+ ProtoLog.d(WM_DEBUG_TASKS, "Reparenting to display area on relaunch: "
+ + "rootTaskId=%d toTop=%b", candidateTask.mTaskId, onTop);
+ }
candidateTask.reparent(this, onTop);
}
}
@@ -1083,8 +1089,19 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Use launch-adjacent-flag-root if launching with launch-adjacent flag.
if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
&& mLaunchAdjacentFlagRootTask != null) {
- final Task launchAdjacentRootAdjacentTask =
- mLaunchAdjacentFlagRootTask.getAdjacentTask();
+ final Task launchAdjacentRootAdjacentTask;
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ final Task[] tmpTask = new Task[1];
+ mLaunchAdjacentFlagRootTask.forOtherAdjacentTasks(task -> {
+ // TODO(b/382208145): enable FLAG_ACTIVITY_LAUNCH_ADJACENT for 3+.
+ // Find the first adjacent for now.
+ tmpTask[0] = task;
+ return true;
+ });
+ launchAdjacentRootAdjacentTask = tmpTask[0];
+ } else {
+ launchAdjacentRootAdjacentTask = mLaunchAdjacentFlagRootTask.getAdjacentTask();
+ }
if (sourceTask != null && (sourceTask == candidateTask
|| sourceTask.topRunningActivity() == null)) {
// Do nothing when task that is getting opened is same as the source or when
@@ -1109,15 +1126,26 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
- final Task adjacentRootTask = launchRootTask != null
- ? launchRootTask.getAdjacentTask() : null;
- if (sourceTask != null && adjacentRootTask != null
- && (sourceTask == adjacentRootTask
- || sourceTask.isDescendantOf(adjacentRootTask))) {
- return adjacentRootTask;
- } else {
+ if (launchRootTask == null || sourceTask == null) {
+ return launchRootTask;
+ }
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final Task adjacentRootTask = launchRootTask.getAdjacentTask();
+ if (adjacentRootTask != null && (sourceTask == adjacentRootTask
+ || sourceTask.isDescendantOf(adjacentRootTask))) {
+ return adjacentRootTask;
+ }
return launchRootTask;
}
+ final Task[] adjacentRootTask = new Task[1];
+ launchRootTask.forOtherAdjacentTasks(task -> {
+ if (sourceTask == task || sourceTask.isDescendantOf(task)) {
+ adjacentRootTask[0] = task;
+ return true;
+ }
+ return false;
+ });
+ return adjacentRootTask[0] != null ? adjacentRootTask[0] : launchRootTask;
}
}
@@ -1128,12 +1156,31 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// A pinned task relaunching should be handled by its task organizer. Skip fallback
// launch target of a pinned task from source task.
|| candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) {
- final Task adjacentTarget = sourceTask.getAdjacentTask();
- if (adjacentTarget != null) {
- if (candidateTask != null
- && (candidateTask == adjacentTarget
- || candidateTask.isDescendantOf(adjacentTarget))) {
- return adjacentTarget;
+ final Task taskWithAdjacent = sourceTask.getTaskWithAdjacent();
+ if (taskWithAdjacent != null) {
+ // Has adjacent.
+ if (candidateTask == null) {
+ return sourceTask.getCreatedByOrganizerTask();
+ }
+ // Check if the candidate is already positioned in the adjacent Task.
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ final Task[] adjacentRootTask = new Task[1];
+ sourceTask.forOtherAdjacentTasks(task -> {
+ if (candidateTask == task || candidateTask.isDescendantOf(task)) {
+ adjacentRootTask[0] = task;
+ return true;
+ }
+ return false;
+ });
+ if (adjacentRootTask[0] != null) {
+ return adjacentRootTask[0];
+ }
+ } else {
+ final Task adjacentTarget = taskWithAdjacent.getAdjacentTask();
+ if (candidateTask == adjacentTarget
+ || candidateTask.isDescendantOf(adjacentTarget)) {
+ return adjacentTarget;
+ }
}
return sourceTask.getCreatedByOrganizerTask();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 51b8bd1f0091..cb6b69072e14 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -94,6 +94,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -239,12 +240,20 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** This task fragment will be removed when the cleanup of its children are done. */
private boolean mIsRemovalRequested;
- /** The TaskFragment that is adjacent to this one. */
+ /** @deprecated b/373709676 replace with {@link #mAdjacentTaskFragments} */
+ @Deprecated
@Nullable
private TaskFragment mAdjacentTaskFragment;
/**
- * Unlike the {@link mAdjacentTaskFragment}, the companion TaskFragment is not always visually
+ * The TaskFragments that are adjacent to each other, including this TaskFragment.
+ * All TaskFragments in this set share the same set instance.
+ */
+ @Nullable
+ private AdjacentSet mAdjacentTaskFragments;
+
+ /**
+ * Unlike the {@link #mAdjacentTaskFragments}, the companion TaskFragment is not always visually
* adjacent to this one, but this TaskFragment will be removed by the organizer if the
* companion TaskFragment is removed.
*/
@@ -442,15 +451,24 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
- if (mAdjacentTaskFragment == taskFragment) {
- return;
- }
- resetAdjacentTaskFragment();
- if (taskFragment != null) {
+ /** @deprecated b/373709676 replace with {@link #setAdjacentTaskFragments}. */
+ @Deprecated
+ void setAdjacentTaskFragment(@NonNull TaskFragment taskFragment) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ if (mAdjacentTaskFragment == taskFragment) {
+ return;
+ }
+ resetAdjacentTaskFragment();
mAdjacentTaskFragment = taskFragment;
taskFragment.setAdjacentTaskFragment(this);
+ return;
}
+
+ setAdjacentTaskFragments(new AdjacentSet(this, taskFragment));
+ }
+
+ void setAdjacentTaskFragments(@NonNull AdjacentSet adjacentTaskFragments) {
+ adjacentTaskFragments.setAsAdjacent();
}
void setCompanionTaskFragment(@Nullable TaskFragment companionTaskFragment) {
@@ -461,7 +479,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return mCompanionTaskFragment;
}
- void resetAdjacentTaskFragment() {
+ /** @deprecated b/373709676 replace with {@link #clearAdjacentTaskFragments()}. */
+ @Deprecated
+ private void resetAdjacentTaskFragment() {
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("resetAdjacentTaskFragment shouldn't be called when"
+ + " allowMultipleAdjacentTaskFragments is enabled. Use either"
+ + " #clearAdjacentTaskFragments or #removeFromAdjacentTaskFragments");
+ }
// Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
@@ -471,6 +496,79 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mDelayLastActivityRemoval = false;
}
+ void clearAdjacentTaskFragments() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ resetAdjacentTaskFragment();
+ return;
+ }
+
+ if (mAdjacentTaskFragments != null) {
+ mAdjacentTaskFragments.clear();
+ }
+ }
+
+ void removeFromAdjacentTaskFragments() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ resetAdjacentTaskFragment();
+ return;
+ }
+
+ if (mAdjacentTaskFragments != null) {
+ mAdjacentTaskFragments.remove(this);
+ }
+ }
+
+ // TODO(b/373709676): update usages.
+ /** @deprecated b/373709676 replace with {@link #getAdjacentTaskFragments()}. */
+ @Deprecated
+ @Nullable
+ TaskFragment getAdjacentTaskFragment() {
+ return mAdjacentTaskFragment;
+ }
+
+ @Nullable
+ AdjacentSet getAdjacentTaskFragments() {
+ return mAdjacentTaskFragments;
+ }
+
+ /**
+ * Runs callback on all TaskFragments that are adjacent to this. The invoke order is not
+ * guaranteed.
+ */
+ void forOtherAdjacentTaskFragments(@NonNull Consumer<TaskFragment> callback) {
+ if (mAdjacentTaskFragments == null) {
+ return;
+ }
+ mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */);
+ }
+
+ /**
+ * Runs callback on all TaskFragments that are adjacent to this. Returns early if callback
+ * returns true on any of them. The invoke order is not guaranteed.
+ */
+ boolean forOtherAdjacentTaskFragments(@NonNull Predicate<TaskFragment> callback) {
+ if (mAdjacentTaskFragments == null) {
+ return false;
+ }
+ return mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */);
+ }
+
+ boolean hasAdjacentTaskFragment() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ return mAdjacentTaskFragment != null;
+ }
+ return mAdjacentTaskFragments != null;
+ }
+
+ boolean isAdjacentTo(@NonNull TaskFragment other) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ return mAdjacentTaskFragment == other;
+ }
+ return other != this
+ && mAdjacentTaskFragments != null
+ && mAdjacentTaskFragments.contains(other);
+ }
+
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@NonNull String processName) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
@@ -566,10 +664,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return isEmbedded() && mPinned;
}
- TaskFragment getAdjacentTaskFragment() {
- return mAdjacentTaskFragment;
- }
-
/** Returns the currently topmost resumed activity. */
@Nullable
ActivityRecord getTopResumedActivity() {
@@ -616,7 +710,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mResumedActivity = r;
final ActivityRecord topResumed = mTaskSupervisor.updateTopResumedActivityIfNeeded(reason);
if (mResumedActivity != null && topResumed != null && topResumed.isEmbedded()
- && topResumed.getTaskFragment().getAdjacentTaskFragment() == this) {
+ && topResumed.getTaskFragment().isAdjacentTo(this)) {
// Explicitly updates the last resumed Activity if the resumed activity is
// adjacent to the top-resumed embedded activity.
mAtmService.setLastResumedActivityUncheckLocked(mResumedActivity, reason);
@@ -2036,7 +2130,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private boolean shouldReportOrientationUnspecified() {
// Apps and their containers are not allowed to specify orientation from adjacent
// TaskFragment.
- return getAdjacentTaskFragment() != null && isVisibleRequested();
+ return hasAdjacentTaskFragment() && isVisibleRequested();
}
@Override
@@ -3086,7 +3180,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
EventLogTags.writeWmTfRemoved(System.identityHashCode(this), getTaskId());
}
mIsRemovalRequested = false;
- resetAdjacentTaskFragment();
+ removeFromAdjacentTaskFragments();
cleanUpEmbeddedTaskFragment();
final boolean shouldExecuteAppTransition =
mClearedTaskFragmentForPip && isTaskVisibleRequested();
@@ -3126,24 +3220,47 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
- final TaskFragment adjacentTf = getAdjacentTaskFragment();
- if (adjacentTf == null) {
+ if (!hasAdjacentTaskFragment()) {
// early return if no adjacent TF.
return false;
}
- if (getParent().mChildren.indexOf(adjacentTf) < getParent().mChildren.indexOf(this)) {
- // early return if this TF already has higher z-ordering.
- return false;
+ final ArrayList<WindowContainer> siblings = getParent().mChildren;
+ final int zOrder = siblings.indexOf(this);
+
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ if (siblings.indexOf(getAdjacentTaskFragment()) < zOrder) {
+ // early return if this TF already has higher z-ordering.
+ return false;
+ }
+ } else {
+ final boolean hasAdjacentOnTop = forOtherAdjacentTaskFragments(
+ tf -> siblings.indexOf(tf) > zOrder);
+ if (!hasAdjacentOnTop) {
+ // early return if this TF already has higher z-ordering.
+ return false;
+ }
}
- ToBooleanFunction<WindowState> getDimBehindWindow =
+ final ToBooleanFunction<WindowState> getDimBehindWindow =
(w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null
&& w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested()
|| w.mActivityRecord.isVisible());
- if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
- // early return if the adjacent Tf has a dimming window.
- return false;
+
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final TaskFragment adjacentTf = getAdjacentTaskFragment();
+ if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
+ // early return if the adjacent Tf has a dimming window.
+ return false;
+ }
+ } else {
+ final boolean adjacentHasDimmingWindow = forOtherAdjacentTaskFragments(tf -> {
+ return tf.forAllWindows(getDimBehindWindow, true);
+ });
+ if (adjacentHasDimmingWindow) {
+ // early return if the adjacent Tf has a dimming window.
+ return false;
+ }
}
// boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
@@ -3267,9 +3384,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
sb.append(" organizerProc=");
sb.append(mTaskFragmentOrganizerProcessName);
}
- if (mAdjacentTaskFragment != null) {
- sb.append(" adjacent=");
- sb.append(mAdjacentTaskFragment);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ if (mAdjacentTaskFragments != null) {
+ sb.append(" adjacent=");
+ sb.append(mAdjacentTaskFragments);
+ }
+ } else {
+ if (mAdjacentTaskFragment != null) {
+ sb.append(" adjacent=");
+ sb.append(mAdjacentTaskFragment);
+ }
}
sb.append('}');
return sb.toString();
@@ -3385,4 +3509,125 @@ class TaskFragment extends WindowContainer<WindowContainer> {
proto.end(token);
}
+
+ /** Set of {@link TaskFragment}s that are adjacent to each other. */
+ static class AdjacentSet {
+ private final ArraySet<TaskFragment> mAdjacentSet;
+
+ AdjacentSet(@NonNull TaskFragment... taskFragments) {
+ this(new ArraySet<>(taskFragments));
+ }
+
+ AdjacentSet(@NonNull ArraySet<TaskFragment> taskFragments) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("allowMultipleAdjacentTaskFragments must be"
+ + " enabled to set more than two TaskFragments adjacent to each other.");
+ }
+ if (taskFragments.size() < 2) {
+ throw new IllegalArgumentException("Adjacent TaskFragments must contain at least"
+ + " two TaskFragments, but only " + taskFragments.size()
+ + " were provided.");
+ }
+ mAdjacentSet = taskFragments;
+ }
+
+ /** Updates each {@link TaskFragment} in the set to be adjacent to each other. */
+ private void setAsAdjacent() {
+ if (mAdjacentSet.isEmpty()
+ || equals(mAdjacentSet.valueAt(0).mAdjacentTaskFragments)) {
+ // No need to update if any TaskFragment in the set has already been updated to the
+ // same set.
+ return;
+ }
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ taskFragment.removeFromAdjacentTaskFragments();
+ taskFragment.mAdjacentTaskFragments = this;
+ }
+ }
+
+ /** Removes the {@link TaskFragment} from the adjacent set. */
+ private void remove(@NonNull TaskFragment taskFragment) {
+ taskFragment.mAdjacentTaskFragments = null;
+ taskFragment.mDelayLastActivityRemoval = false;
+ mAdjacentSet.remove(taskFragment);
+ if (mAdjacentSet.size() < 2) {
+ // To be considered as adjacent, there must be at least 2 TaskFragments in the set.
+ clear();
+ }
+ }
+
+ /** Clears the adjacent relationship. */
+ private void clear() {
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ // Clear all reference.
+ taskFragment.mAdjacentTaskFragments = null;
+ taskFragment.mDelayLastActivityRemoval = false;
+ }
+ mAdjacentSet.clear();
+ }
+
+ /** Whether the {@link TaskFragment} is in this adjacent set. */
+ boolean contains(@NonNull TaskFragment taskFragment) {
+ return mAdjacentSet.contains(taskFragment);
+ }
+
+ /**
+ * Runs the callback on all adjacent TaskFragments. Skips the exclude one if not null.
+ */
+ void forAllTaskFragments(@NonNull Consumer<TaskFragment> callback,
+ @Nullable TaskFragment exclude) {
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ if (taskFragment != exclude) {
+ callback.accept(taskFragment);
+ }
+ }
+ }
+
+ /**
+ * Runs the callback on all adjacent TaskFragments until one returns {@code true}. Skips the
+ * exclude one if not null.
+ */
+ boolean forAllTaskFragments(@NonNull Predicate<TaskFragment> callback,
+ @Nullable TaskFragment exclude) {
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ if (taskFragment == exclude) {
+ continue;
+ }
+ if (callback.test(taskFragment)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AdjacentSet other)) {
+ return false;
+ }
+ return mAdjacentSet.equals(other.mAdjacentSet);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AdjacentSet{");
+ final int size = mAdjacentSet.size();
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(mAdjacentSet.valueAt(i));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 64b9df59990b..cc957bd9ee42 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -48,26 +48,37 @@ class TaskSnapshotCache extends SnapshotCache<Task> {
}
/**
- * If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW MANAGER LOCK!
+ * Retrieves a snapshot from cache.
*/
- @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
- boolean isLowResolution) {
- final TaskSnapshot snapshot = getSnapshot(taskId);
- if (snapshot != null) {
- return snapshot;
- }
+ @Nullable TaskSnapshot getSnapshot(int taskId, boolean isLowResolution) {
+ return getSnapshot(taskId, isLowResolution, TaskSnapshot.REFERENCE_NONE);
+ }
- // Try to restore from disk if asked.
- if (!restoreFromDisk) {
- return null;
+ // TODO (b/238206323) Respect isLowResolution.
+ @Nullable TaskSnapshot getSnapshot(int taskId, boolean isLowResolution,
+ @TaskSnapshot.ReferenceFlags int usage) {
+ synchronized (mLock) {
+ final TaskSnapshot snapshot = getSnapshotInner(taskId);
+ if (snapshot != null) {
+ if (usage != TaskSnapshot.REFERENCE_NONE) {
+ snapshot.addReference(usage);
+ }
+ return snapshot;
+ }
}
- return tryRestoreFromDisk(taskId, userId, isLowResolution);
+ return null;
}
/**
- * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD!
+ * Restore snapshot from disk, DO NOT HOLD THE WINDOW MANAGER LOCK!
*/
- private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean isLowResolution) {
- return mLoader.loadTask(taskId, userId, isLowResolution);
+ @Nullable TaskSnapshot getSnapshotFromDisk(int taskId, int userId, boolean isLowResolution,
+ @TaskSnapshot.ReferenceFlags int usage) {
+ final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, isLowResolution);
+ // Note: This can be weird if the caller didn't ask for reference.
+ if (snapshot != null && usage != TaskSnapshot.REFERENCE_NONE) {
+ snapshot.addReference(usage);
+ }
+ return snapshot;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index c130931277fe..38a2ebeba332 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -174,14 +174,32 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
}
/**
- * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW
- * MANAGER LOCK WHEN CALLING THIS METHOD!
+ * Retrieves a snapshot from cache.
*/
@Nullable
- TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
- boolean isLowResolution) {
- return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
- && mPersistInfoProvider.enableLowResSnapshots());
+ TaskSnapshot getSnapshot(int taskId, boolean isLowResolution) {
+ return getSnapshot(taskId, false /* isLowResolution */, TaskSnapshot.REFERENCE_NONE);
+ }
+
+ /**
+ * Retrieves a snapshot from cache.
+ */
+ @Nullable
+ TaskSnapshot getSnapshot(int taskId, boolean isLowResolution,
+ @TaskSnapshot.ReferenceFlags int usage) {
+ return mCache.getSnapshot(taskId, isLowResolution
+ && mPersistInfoProvider.enableLowResSnapshots(), usage);
+ }
+
+ /**
+ * Retrieves a snapshot from disk.
+ * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD!
+ */
+ @Nullable
+ TaskSnapshot getSnapshotFromDisk(int taskId, int userId,
+ boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) {
+ return mCache.getSnapshotFromDisk(taskId, userId, isLowResolution
+ && mPersistInfoProvider.enableLowResSnapshots(), usage);
}
/**
@@ -189,7 +207,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
* last taken, or -1 if no such snapshot exists for that task.
*/
long getSnapshotCaptureTime(int taskId) {
- final TaskSnapshot snapshot = mCache.getSnapshot(taskId);
+ final TaskSnapshot snapshot = mCache.getSnapshot(taskId, false /* isLowResolution */);
if (snapshot != null) {
return snapshot.getCaptureTime();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1fc609b7d03a..b4a22b0dd034 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1870,7 +1870,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final DisplayArea<?> da = wc.asDisplayArea();
if (da == null) continue;
if (da.isVisibleRequested()) {
- mController.mValidateDisplayVis.remove(da);
+ final int inValidateList = mController.mValidateDisplayVis.indexOf(da);
+ if (inValidateList >= 0
+ // The display-area is visible, but if we only detect a non-visibility
+ // change, then we shouldn't remove the validator.
+ && !mChanges.get(da).mVisible) {
+ mController.mValidateDisplayVis.remove(inValidateList);
+ }
} else {
// In case something accidentally hides a displayarea and nothing shows it again.
mController.mValidateDisplayVis.add(da);
@@ -4144,7 +4150,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
.setSourceCrop(cropBounds)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
- .setHintForSeamlessTransition(isDisplayRotation)
+ // We always reroute this screenshot to the display, so this transition
+ // is ALWAYS seamless
+ .setHintForSeamlessTransition(true)
.build();
ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
ScreenCapture.captureLayers(captureArgs);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8562bb23b30f..f3c03cbfb3b4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -791,19 +791,30 @@ class TransitionController {
ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
ActivityManager.RunningTaskInfo startTaskInfo = null;
- ActivityManager.RunningTaskInfo pipTaskInfo = null;
+ TransitionRequestInfo.PipChange pipChange = null;
if (startTask != null) {
startTaskInfo = startTask.getTaskInfo();
}
// set the pip task in the request if provided
if (transition.getPipActivity() != null) {
- pipTaskInfo = transition.getPipActivity().getTask().getTaskInfo();
+ ActivityManager.RunningTaskInfo pipTaskInfo =
+ transition.getPipActivity().getTask().getTaskInfo();
+ ActivityRecord pipActivity = transition.getPipActivity();
+ if (pipActivity.getTaskFragment() != null
+ && pipActivity.getTaskFragment() != pipActivity.getTask()) {
+ // If the PiP activity is in a TF different from its task, this could be
+ // AE-to-PiP case, so PipChange will have the TF token cached separately.
+ pipChange = new TransitionRequestInfo.PipChange(pipActivity.getTaskFragment()
+ .mRemoteToken.toWindowContainerToken(), pipTaskInfo);
+ } else {
+ pipChange = new TransitionRequestInfo.PipChange(pipTaskInfo);
+ }
transition.setPipActivity(null);
}
final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
- startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
+ startTaskInfo, pipChange, remoteTransition, displayChange,
transition.getFlags(), transition.getSyncId());
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2397e032fcc8..aa60f939f9aa 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3733,7 +3733,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
&& !mTransitionController.useShellTransitionsRotation()) {
if (deltaRotation != Surface.ROTATION_0) {
updateSurfaceRotation(t, deltaRotation, null /* positionLeash */);
- getPendingTransaction().setFixedTransformHint(mSurfaceControl,
+ mDisplayContent.setFixedTransformHint(getPendingTransaction(), mSurfaceControl,
getWindowConfiguration().getDisplayRotation());
} else if (deltaRotation != mLastDeltaRotation) {
t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 809745e48300..6d0da1fd36f5 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -167,6 +167,22 @@ class WindowContextListenerController {
return true;
}
+ boolean assertCallerCanReparentListener(@NonNull IBinder clientToken,
+ boolean callerCanManageAppTokens, int callingUid, int displayId) {
+ if (!assertCallerCanModifyListener(clientToken, callerCanManageAppTokens, callingUid)) {
+ return false;
+ }
+
+ final WindowContainer<?> container = getContainer(clientToken);
+ if (container != null && container.getDisplayContent() != null
+ && container.getDisplayContent().mDisplayId == displayId) {
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE,
+ "The listener has already been attached to the same display id");
+ return false;
+ }
+ return true;
+ }
+
boolean hasListener(IBinder clientToken) {
return mListeners.containsKey(clientToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 3ad9b62ef058..9a5c8dffc0fc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -42,12 +42,23 @@ final class WindowManagerConstants {
* <ul>
* <li>false: applies to no apps (default)</li>
* <li>true: applies to all apps</li>
- * <li>large: applies to all apps but only on large screens</li>
* </ul>
*/
private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
"ignore_activity_orientation_request";
+ /**
+ * The orientation of activity will be always "unspecified" except for game apps.
+ * <p>Possible values:
+ * <ul>
+ * <li>none: applies to no apps (default)</li>
+ * <li>all: applies to all apps ({@see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST})</li>
+ * <li>large: applies to all apps but only on large screens</li>
+ * </ul>
+ */
+ private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST_SCREENS =
+ "ignore_activity_orientation_request_screens";
+
/** The packages that ignore {@link #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST}. */
private static final String KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST =
"opt_out_ignore_activity_orientation_request_list";
@@ -155,6 +166,7 @@ final class WindowManagerConstants {
updateSystemGestureExclusionLogDebounceMillis();
break;
case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST:
+ case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST_SCREENS:
updateIgnoreActivityOrientationRequest();
break;
case KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST:
@@ -186,12 +198,16 @@ final class WindowManagerConstants {
}
private void updateIgnoreActivityOrientationRequest() {
- final String value = mDeviceConfig.getProperty(
+ boolean allScreens = mDeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
+ String whichScreens = mDeviceConfig.getProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
- mIgnoreActivityOrientationRequestSmallScreen = Boolean.parseBoolean(value);
- mIgnoreActivityOrientationRequestLargeScreen = mIgnoreActivityOrientationRequestSmallScreen
- || ("large".equals(value));
+ KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST_SCREENS);
+ allScreens |= ("all".equalsIgnoreCase(whichScreens));
+ boolean largeScreens = allScreens || ("large".equalsIgnoreCase(whichScreens));
+ mIgnoreActivityOrientationRequestSmallScreen = allScreens;
+ mIgnoreActivityOrientationRequestLargeScreen = largeScreens;
}
private void updateOptOutIgnoreActivityOrientationRequestList() {
@@ -221,9 +237,9 @@ final class WindowManagerConstants {
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
- pw.print(" "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
- pw.print("="); pw.println(mIgnoreActivityOrientationRequestSmallScreen ? "true"
- : mIgnoreActivityOrientationRequestLargeScreen ? "large" : "false");
+ pw.print(" "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST_SCREENS);
+ pw.print("="); pw.println(mIgnoreActivityOrientationRequestSmallScreen ? "all"
+ : mIgnoreActivityOrientationRequestLargeScreen ? "large" : "none");
if (mOptOutIgnoreActivityOrientationRequestPackages != null) {
pw.print(" "); pw.print(KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST);
pw.print("="); pw.println(mOptOutIgnoreActivityOrientationRequestPackages);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 00ade8088e82..9d9c53dfe0f4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -358,6 +358,7 @@ import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
+import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -2672,7 +2673,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (outRelayoutResult != null) {
if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
- && win.mSyncSeqId > lastSyncSeqId) {
+ && win.mSyncSeqId > lastSyncSeqId && !displayContent.mWaitingForConfig) {
outRelayoutResult.syncSeqId = win.shouldSyncWithBuffers()
? win.mSyncSeqId
: -1;
@@ -3042,6 +3043,81 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public boolean reparentWindowContextToDisplayArea(
+ @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
+ if (!Flags.reparentWindowTokenApi()) {
+ return false;
+ }
+ Objects.requireNonNull(appThread);
+ Objects.requireNonNull(clientToken);
+ final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
+ "reparentWindowContextToDisplayArea", false /* printLog */);
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final WindowProcessController wpc = mAtmService.getProcessController(appThread);
+ if (wpc == null) {
+ ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: calling from"
+ + " non-existing process pid=%d uid=%d", callingPid, callingUid);
+ return false;
+ }
+ final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: trying to attach"
+ + " to a non-existing display:%d", displayId);
+ return false;
+ }
+
+ if (!mWindowContextListenerController.assertCallerCanReparentListener(clientToken,
+ callerCanManageAppTokens, callingUid, displayId)) {
+ return false;
+ }
+ final WindowContainer<?> container = mWindowContextListenerController.getContainer(
+ clientToken);
+
+ final WindowToken token = container != null ? container.asWindowToken() : null;
+ if (token != null && token.isFromClient()) {
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Reparenting from dc to displayId=%d",
+ displayId);
+ // Reparent the window created for this window context.
+ dc.reParentWindowToken(token);
+ hideUntilNextDraw(token);
+ // This makes sure there is a traversal scheduled that will eventually report
+ // the window resize to the client.
+ dc.setLayoutNeeded();
+ requestTraversal();
+ return true;
+ }
+
+ final int type = mWindowContextListenerController.getWindowType(clientToken);
+ final Bundle options = mWindowContextListenerController.getOptions(clientToken);
+ // No window yet, switch listening DA.
+ final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
+ callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
+ da, type, options, true /* shouldDispatchConfigWhenRegistering */);
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private void hideUntilNextDraw(@NonNull WindowToken token) {
+ final WindowState topChild = token.getTopChild();
+ if (topChild != null) {
+ mTransactionFactory.get().hide(token.mSurfaceControl).apply();
+ topChild.applyWithNextDraw(t -> {
+ if (token.mSurfaceControl != null) {
+ t.show(token.mSurfaceControl);
+ }
+ });
+ }
+ }
+
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
@@ -4216,16 +4292,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Retrieves a snapshot. If restoreFromDisk equals equals {@code true}, DO NOT HOLD THE WINDOW
- * MANAGER LOCK WHEN CALLING THIS METHOD!
- */
- public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean isLowResolution,
- boolean restoreFromDisk) {
- return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk,
- isLowResolution);
- }
-
- /**
* Generates and returns an up-to-date {@link Bitmap} for the specified taskId.
*
* @param taskId The task ID of the task for which a Bitmap is requested.
@@ -4671,7 +4737,8 @@ public class WindowManagerService extends IWindowManager.Stub
@EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
@Override
public void updateDisplayWindowRequestedVisibleTypes(int displayId,
- @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token statsToken) {
+ @InsetsType int visibleTypes, @InsetsType int mask,
+ @Nullable ImeTracker.Token statsToken) {
updateDisplayWindowRequestedVisibleTypes_enforcePermission();
final long origId = Binder.clearCallingIdentity();
try {
@@ -4684,7 +4751,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
- dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes);
+ dc.mRemoteInsetsControlTarget.updateRequestedVisibleTypes(visibleTypes, mask);
// TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
// IME provider. Check if we have to create a new request here, if null.
dc.getInsetsStateController().onRequestedVisibleTypesChanged(
@@ -9019,16 +9086,19 @@ public class WindowManagerService extends IWindowManager.Stub
clearPointerDownOutsideFocusRunnable();
+ final InputTarget focusedInputTarget = mFocusedInputTarget;
if (shouldDelayTouchOutside(t)) {
- mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
+ mPointerDownOutsideFocusRunnable =
+ () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
mH.postDelayed(mPointerDownOutsideFocusRunnable, POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS);
} else if (!fromHandler) {
// Still post the runnable to handler thread in case there is already a runnable
// in execution, but still waiting to hold the wm lock.
- mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
+ mPointerDownOutsideFocusRunnable =
+ () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
mH.post(mPointerDownOutsideFocusRunnable);
} else {
- handlePointerDownOutsideFocus(t);
+ handlePointerDownOutsideFocus(t, focusedInputTarget);
}
}
@@ -9060,8 +9130,19 @@ public class WindowManagerService extends IWindowManager.Stub
return shouldDelayTouchForEmbeddedActivity || shouldDelayTouchForFreeform;
}
- private void handlePointerDownOutsideFocus(InputTarget t) {
+ private void handlePointerDownOutsideFocus(InputTarget t, InputTarget focusedInputTarget) {
synchronized (mGlobalLock) {
+ final WindowState w = t.getWindowState();
+ // Skip if the mFocusedInputTarget is already changed or the touched Activity is no
+ // longer visible. This is possible if the pointer-down-outside-focus event is
+ // delayed to be handled.
+ if (mFocusedInputTarget != focusedInputTarget || (w != null
+ && w.getActivityRecord() != null
+ && !w.getActivityRecord().isVisibleRequested())) {
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
+ "Skip onPointerDownOutsideFocusLocked due to input target changed %s", t);
+ return;
+ }
if (mPointerDownOutsideFocusRunnable != null
&& mH.hasCallbacks(mPointerDownOutsideFocusRunnable)) {
// Skip if there's another pending pointer-down-outside-focus event.
@@ -9069,7 +9150,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
clearPointerDownOutsideFocusRunnable();
- final WindowState w = t.getWindowState();
if (w != null) {
final Task task = w.getTask();
if (task != null && w.mTransitionController.isTransientHide(task)) {
@@ -9934,11 +10014,10 @@ public class WindowManagerService extends IWindowManager.Stub
&& imeTargetWindow.mActivityRecord.mLastImeShown) {
return true;
}
+ final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(
+ imeTargetWindowTask.mTaskId, false /* isLowResolution */);
+ return snapshot != null && snapshot.hasImeSurface();
}
- final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
- imeTargetWindowTask.mUserId, false /* isLowResolution */,
- false /* restoreFromDisk */);
- return snapshot != null && snapshot.hasImeSurface();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ddff24d35232..fb197c566b7d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1155,7 +1155,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.getAdjacentTaskFragment() == null && !clearRoot) {
+ } else if (!task.hasAdjacentTaskFragment() && !clearRoot) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
@@ -1335,11 +1335,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK: {
final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
- Task pipTask = container.asTask();
- if (pipTask == null) {
+ TaskFragment pipTaskFragment = container.asTaskFragment();
+ if (pipTaskFragment == null) {
break;
}
- ActivityRecord pipActivity = pipTask.getActivity(
+ ActivityRecord pipActivity = pipTaskFragment.getActivity(
(activity) -> activity.pictureInPictureArgs != null);
if (pipActivity.isState(RESUMED)) {
@@ -1645,9 +1645,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
opType, exception);
break;
}
- if (taskFragment.getAdjacentTaskFragment() != secondaryTaskFragment) {
+ if (!taskFragment.isAdjacentTo(secondaryTaskFragment)) {
// Only have lifecycle effect if the adjacent changed.
- taskFragment.setAdjacentTaskFragment(secondaryTaskFragment);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ // Activity Embedding only set two TFs adjacent.
+ taskFragment.setAdjacentTaskFragments(
+ new TaskFragment.AdjacentSet(taskFragment, secondaryTaskFragment));
+ } else {
+ taskFragment.setAdjacentTaskFragment(secondaryTaskFragment);
+ }
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -1663,21 +1669,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
case OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS: {
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- if (adjacentTaskFragment == null) {
+ if (!taskFragment.hasAdjacentTaskFragment()) {
break;
}
- taskFragment.resetAdjacentTaskFragment();
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- // Clear the focused app if the focused app is no longer visible after reset the
- // adjacent TaskFragments.
+ // Check if the focused app is in the adjacent set that will be cleared.
final ActivityRecord focusedApp = taskFragment.getDisplayContent().mFocusedApp;
final TaskFragment focusedTaskFragment = focusedApp != null
? focusedApp.getTaskFragment()
: null;
- if ((focusedTaskFragment == taskFragment
- || focusedTaskFragment == adjacentTaskFragment)
+ final boolean wasFocusedInAdjacent = focusedTaskFragment == taskFragment
+ || (focusedTaskFragment != null
+ && taskFragment.isAdjacentTo(focusedTaskFragment));
+
+ taskFragment.removeFromAdjacentTaskFragments();
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+
+ // Clear the focused app if the focused app is no longer visible after reset the
+ // adjacent TaskFragments.
+ if (wasFocusedInAdjacent
&& !focusedTaskFragment.shouldBeVisible(null /* starting */)) {
focusedTaskFragment.getDisplayContent().setFocusedApp(null /* newFocus */);
}
@@ -2191,26 +2201,60 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
- final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer());
- if (wc1 == null || !wc1.isAttached()) {
- Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1);
- return TRANSACT_EFFECTS_NONE;
- }
- final TaskFragment root1 = wc1.asTaskFragment();
- final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot());
- if (wc2 == null || !wc2.isAttached()) {
- Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2);
- return TRANSACT_EFFECTS_NONE;
- }
- final TaskFragment root2 = wc2.asTaskFragment();
- if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
- throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
- + " organizer root1=" + root1 + " root2=" + root2);
- }
- if (root1.getAdjacentTaskFragment() == root2) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer());
+ if (wc1 == null || !wc1.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final TaskFragment root1 = wc1.asTaskFragment();
+ final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot());
+ if (wc2 == null || !wc2.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final TaskFragment root2 = wc2.asTaskFragment();
+ if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
+ throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ + " organizer root1=" + root1 + " root2=" + root2);
+ }
+ if (root1.isAdjacentTo(root2)) {
+ return TRANSACT_EFFECTS_NONE;
+ }
+ root1.setAdjacentTaskFragment(root2);
+ return TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
+ final IBinder[] containers = hop.getContainers();
+ final ArraySet<TaskFragment> adjacentRoots = new ArraySet<>();
+ for (IBinder container : containers) {
+ final WindowContainer wc = WindowContainer.fromBinder(container);
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final Task root = wc.asTask();
+ if (root == null) {
+ // Only support Task. Use WCT#setAdjacentTaskFragments for non-Task TaskFragment.
+ throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not called with"
+ + " Task. wc=" + wc);
+ }
+ if (!root.mCreatedByOrganizer) {
+ throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ + " organizer root=" + root);
+ }
+ if (adjacentRoots.contains(root)) {
+ throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: called with same"
+ + " root twice=" + root);
+ }
+ adjacentRoots.add(root);
+ }
+ final TaskFragment root0 = adjacentRoots.valueAt(0);
+ final TaskFragment.AdjacentSet adjacentSet = new TaskFragment.AdjacentSet(adjacentRoots);
+ if (adjacentSet.equals(root0.getAdjacentTaskFragments())) {
return TRANSACT_EFFECTS_NONE;
}
- root1.setAdjacentTaskFragment(root2);
+ root0.setAdjacentTaskFragments(adjacentSet);
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -2225,10 +2269,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by"
+ " organizer root=" + root);
}
- if (root.getAdjacentTaskFragment() == null) {
+ if (!root.hasAdjacentTaskFragment()) {
return TRANSACT_EFFECTS_NONE;
}
- root.resetAdjacentTaskFragment();
+ root.removeFromAdjacentTaskFragments();
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 7f0c33657144..80e4c30b73f8 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1263,7 +1263,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
&& com.android.window.flags.Flags
.processPriorityPolicyForMultiWindowMode()
- && task.getAdjacentTask() != null) {
+ && task.hasAdjacentTask()) {
stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN;
} else if (windowingMode == WINDOWING_MODE_FREEFORM) {
hasResumedFreeform = true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cebe790bb1b9..68b4b6f0ae91 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1631,8 +1631,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
final InsetsState rawInsetsState =
mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
- final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(
- mAttrs, getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
+ final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(this,
+ rawInsetsState);
return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
includeTransient);
}
@@ -3303,7 +3303,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// just kill it. And if it is a window of foreground activity, the activity can be
// restarted automatically if needed.
Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
- if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+ if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid
+ && android.os.Process.getThreadGroupLeader(mSession.mPid) == mSession.mPid) {
android.os.Process.killProcess(mSession.mPid);
}
}
@@ -3311,8 +3312,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Because the client is notified to be invisible, it should no longer be considered as
// drawn state. This prevent the app from showing incomplete content if the app is
// requested to be visible in a short time (e.g. before activity stopped).
- if (Flags.resetDrawStateOnClientInvisible() && !clientVisible && mActivityRecord != null
- && mWinAnimator.mDrawState == HAS_DRAWN) {
+ if (!clientVisible && mActivityRecord != null && mWinAnimator.mDrawState == HAS_DRAWN) {
mWinAnimator.resetDrawState();
// Make sure the app can report drawn if it becomes visible again.
forceReportingResized();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 004f406035c0..cca73c574951 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -49,6 +49,7 @@ import android.window.WindowContext;
import com.android.internal.protolog.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -386,7 +387,15 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void onDisplayChanged(DisplayContent dc) {
- dc.reParentWindowToken(this);
+ if (!Flags.reparentWindowTokenApi()) {
+ dc.reParentWindowToken(this);
+ } else {
+ // This check is needed to break recursion, as DisplayContent#reparentWindowToken also
+ // triggers a WindowToken#onDisplayChanged.
+ if (dc.getWindowToken(token) == null) {
+ dc.reParentWindowToken(this);
+ }
+ }
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
@@ -629,7 +638,7 @@ class WindowToken extends WindowContainer<WindowState> {
.build();
t.setPosition(leash, mLastSurfacePosition.x, mLastSurfacePosition.y);
t.reparent(getSurfaceControl(), leash);
- getPendingTransaction().setFixedTransformHint(leash,
+ mDisplayContent.setFixedTransformHint(getPendingTransaction(), leash,
getWindowConfiguration().getDisplayRotation());
mFixedRotationTransformLeash = leash;
updateSurfaceRotation(t, rotation, mFixedRotationTransformLeash);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4c0cee404b68..01639cc3b516 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -11,7 +11,6 @@ cc_library_static {
name: "libservices.core",
defaults: ["libservices.core-libs"],
- cpp_std: "c++2a",
cflags: [
"-Wall",
"-Werror",
@@ -62,6 +61,7 @@ cc_library_static {
"com_android_server_SystemServer.cpp",
"com_android_server_tv_TvUinputBridge.cpp",
"com_android_server_tv_TvInputHal.cpp",
+ "com_android_server_UsbAlsaDevice.cpp",
"com_android_server_UsbAlsaJackDetector.cpp",
"com_android_server_UsbAlsaMidiDevice.cpp",
"com_android_server_UsbDeviceManager.cpp",
diff --git a/services/core/jni/com_android_server_UsbAlsaDevice.cpp b/services/core/jni/com_android_server_UsbAlsaDevice.cpp
new file mode 100644
index 000000000000..166932f167ed
--- /dev/null
+++ b/services/core/jni/com_android_server_UsbAlsaDevice.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UsbAlsaDeviceJNI"
+
+#include <nativehelper/JNIPlatformHelp.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <string>
+#include <vector>
+
+#include "jni.h"
+#include "utils/Log.h"
+
+static const std::vector<std::string> POSSIBLE_HARDWARE_VOLUME_MIXER_NAMES =
+ {"Headphone Playback Volume", "Headset Playback Volume", "PCM Playback Volume"};
+
+namespace android {
+
+static void android_server_UsbAlsaDevice_setVolume(JNIEnv* /*env*/, jobject /*thiz*/, jint card,
+ float volume) {
+ ALOGD("%s(%d, %f)", __func__, card, volume);
+ struct mixer* alsaMixer = mixer_open(card);
+ if (alsaMixer == nullptr) {
+ ALOGW("%s(%d, %f) returned as no mixer is opened", __func__, card, volume);
+ return;
+ }
+ struct mixer_ctl* ctl = nullptr;
+ for (const auto& mixerName : POSSIBLE_HARDWARE_VOLUME_MIXER_NAMES) {
+ ctl = mixer_get_ctl_by_name(alsaMixer, mixerName.c_str());
+ if (ctl != nullptr) {
+ break;
+ }
+ }
+ if (ctl == nullptr) {
+ ALOGW("%s(%d, %f) returned as no volume mixer is found", __func__, card, volume);
+ return;
+ }
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_percent(ctl, id, 100 * volume); error != 0) {
+ ALOGE("%s(%d, %f) failed, error=%d", __func__, card, volume, error);
+ return;
+ }
+ }
+ ALOGD("%s(%d, %f) succeed", __func__, card, volume);
+}
+
+static JNINativeMethod method_table[] = {
+ {"nativeSetVolume", "(IF)V", (void*)android_server_UsbAlsaDevice_setVolume},
+};
+
+int register_android_server_UsbAlsaDevice(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaDevice", method_table,
+ NELEM(method_table));
+}
+} // namespace android
diff --git a/services/core/jni/com_android_server_am_Freezer.cpp b/services/core/jni/com_android_server_am_Freezer.cpp
index 81487281dee7..e9a99f0fab64 100644
--- a/services/core/jni/com_android_server_am_Freezer.cpp
+++ b/services/core/jni/com_android_server_am_Freezer.cpp
@@ -68,7 +68,7 @@ jint getBinderFreezeInfo(JNIEnv *env, jobject, jint pid) {
bool isFreezerSupported(JNIEnv *env, jclass) {
std::string path;
- if (!getAttributePathForTask("FreezerState", getpid(), &path)) {
+ if (!CgroupGetAttributePathForTask("FreezerState", getpid(), &path)) {
ALOGI("No attribute for FreezerState");
return false;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5a457302108c..04642302ce45 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -68,6 +68,7 @@
#include <map>
#include <vector>
+#include "android_hardware_display_DisplayTopology.h"
#include "android_hardware_display_DisplayViewport.h"
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_hardware_input_InputWindowHandle.h"
@@ -321,6 +322,8 @@ public:
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
+ void setDisplayTopology(JNIEnv* env, jobject topologyGraph);
+
base::Result<std::unique_ptr<InputChannel>> createInputChannel(const std::string& name);
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(ui::LogicalDisplayId displayId,
const std::string& name,
@@ -640,6 +643,11 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
InputReaderConfiguration::Change::DISPLAY_INFO);
}
+void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph) {
+ android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+ // TODO(b/367661489): Use the topology
+}
+
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
const std::string& name) {
ATRACE_CALL();
@@ -2092,6 +2100,12 @@ static void nativeSetDisplayViewports(JNIEnv* env, jobject nativeImplObj,
im->setDisplayViewports(env, viewportObjArray);
}
+static void nativeSetDisplayTopology(JNIEnv* env, jobject nativeImplObj,
+ jobject displayTopologyObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->setDisplayTopology(env, displayTopologyObj);
+}
+
static jint nativeGetScanCodeState(JNIEnv* env, jobject nativeImplObj, jint deviceId,
jint sourceMask, jint scanCode) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3148,6 +3162,8 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"start", "()V", (void*)nativeStart},
{"setDisplayViewports", "([Landroid/hardware/display/DisplayViewport;)V",
(void*)nativeSetDisplayViewports},
+ {"setDisplayTopology", "(Landroid/hardware/display/DisplayTopologyGraph;)V",
+ (void*)nativeSetDisplayTopology},
{"getScanCodeState", "(III)I", (void*)nativeGetScanCodeState},
{"getKeyCodeState", "(III)I", (void*)nativeGetKeyCodeState},
{"getSwitchState", "(III)I", (void*)nativeGetSwitchState},
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index df37ec3ef037..e3bd69c30de7 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -33,6 +33,7 @@ int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv* env);
int register_android_server_HintManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_UsbAlsaDevice(JNIEnv* env);
int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
int register_android_server_UsbAlsaMidiDevice(JNIEnv* env);
int register_android_server_UsbDeviceManager(JavaVM* vm, JNIEnv* env);
@@ -49,6 +50,7 @@ int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_hardware_display_DisplayTopology(JNIEnv* env);
int register_android_server_am_OomConnection(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_Freezer(JNIEnv* env);
@@ -97,6 +99,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_UsbDeviceManager(vm, env);
+ register_android_server_UsbAlsaDevice(env);
register_android_server_UsbAlsaJackDetector(env);
register_android_server_UsbAlsaMidiDevice(env);
register_android_server_UsbHostManager(env);
@@ -114,6 +117,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_hardware_display_DisplayViewport(env);
+ register_android_hardware_display_DisplayTopology(env);
register_android_server_am_OomConnection(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_Freezer(env);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index e73dacbed9a3..8533eafaf9e0 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.credentials.selection.DisabledProviderData;
import android.credentials.selection.IntentCreationResult;
import android.credentials.selection.IntentFactory;
@@ -46,6 +47,12 @@ import java.util.UUID;
/** Initiates the Credential Manager UI and receives results. */
public class CredentialManagerUi {
+
+ private static final String SESSION_ID_TRACK_ONE =
+ "com.android.server.credentials.CredentialManagerUi.SESSION_ID_TRACK_ONE";
+ private static final String SESSION_ID_TRACK_TWO =
+ "com.android.server.credentials.CredentialManagerUi.SESSION_ID_TRACK_TWO";
+
@NonNull
private final CredentialManagerUiCallback mCallbacks;
@NonNull
@@ -107,7 +114,7 @@ public class CredentialManagerUi {
/** Creates intent that is ot be invoked to cancel an in-progress UI session. */
public Intent createCancelIntent(IBinder requestId, String packageName) {
return IntentFactory.createCancelUiIntent(mContext, requestId,
- /*shouldShowCancellationUi=*/ true, packageName);
+ /*shouldShowCancellationUi=*/ true, packageName, mUserId);
}
/**
@@ -148,8 +155,8 @@ public class CredentialManagerUi {
* by the calling app process. The bottom-sheet navigates to the default page when the intent
* is invoked.
*
- * @param requestInfo the information about the request
- * @param providerDataList the list of provider data from remote providers
+ * @param requestInfo the information about the request
+ * @param providerDataList the list of provider data from remote providers
*/
public PendingIntent createPendingIntent(
RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
@@ -170,11 +177,16 @@ public class CredentialManagerUi {
IntentCreationResult intentCreationResult = IntentFactory
.createCredentialSelectorIntentForCredMan(mContext, requestInfo, providerDataList,
- new ArrayList<>(disabledProviderDataList), mResultReceiver);
+ new ArrayList<>(disabledProviderDataList), mResultReceiver, mUserId);
requestSessionMetric.collectUiConfigurationResults(
mContext, intentCreationResult, mUserId);
Intent intent = intentCreationResult.getIntent();
intent.setAction(UUID.randomUUID().toString());
+ if (Flags.frameworkSessionIdMetricBundle()) {
+ intent.putExtra(SESSION_ID_TRACK_ONE,
+ requestSessionMetric.getInitialPhaseMetric().getSessionIdCaller());
+ intent.putExtra(SESSION_ID_TRACK_TWO, requestSessionMetric.getSessionIdTrackTwo());
+ }
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
return PendingIntent.getActivityAsUser(
@@ -192,14 +204,14 @@ public class CredentialManagerUi {
* each autofill id and passed in as extras in the pending intent set as authentication
* of the pinned entry.
*
- * @param requestInfo the information about the request
- * @param requestSessionMetric the metric object for logging
+ * @param requestInfo the information about the request
+ * @param requestSessionMetric the metric object for logging
*/
public Intent createIntentForAutofill(RequestInfo requestInfo,
RequestSessionMetric requestSessionMetric) {
IntentCreationResult intentCreationResult = IntentFactory
.createCredentialSelectorIntentForAutofill(mContext, requestInfo, new ArrayList<>(),
- mResultReceiver);
+ mResultReceiver, mUserId);
requestSessionMetric.collectUiConfigurationResults(
mContext, intentCreationResult, mUserId);
return intentCreationResult.getIntent();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3ccde0643eaf..2627895b8c63 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -26,6 +26,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT;
@@ -126,6 +127,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEV
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
+import static android.app.admin.DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
import static android.app.admin.DevicePolicyManager.ContentProtectionPolicy;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
@@ -332,6 +334,7 @@ import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyDrawableResource;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
@@ -9086,7 +9089,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
CallerIdentity caller = getCallerIdentity(who);
if (Flags.setAutoTimeEnabledCoexistence()) {
- Preconditions.checkCallAuthorization(hasPermission(SET_TIME, caller.getPackageName()));
+ Preconditions.checkCallAuthorization(hasPermission(SET_TIME, callerPackageName));
} else {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
@@ -9149,7 +9152,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* who */ null,
SET_TIME,
- caller.getPackageName(),
+ callerPackageName,
UserHandle.USER_ALL
);
Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
@@ -9194,7 +9197,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
CallerIdentity caller = getCallerIdentity(who);
if (Flags.setAutoTimeZoneEnabledCoexistence()) {
Preconditions.checkCallAuthorization(
- hasPermission(SET_TIME_ZONE, caller.getPackageName()));
+ hasPermission(SET_TIME_ZONE, callerPackageName));
} else {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
@@ -9252,7 +9255,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* who */ null,
SET_TIME_ZONE,
- caller.getPackageName(),
+ callerPackageName,
UserHandle.USER_ALL
);
Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
@@ -22974,6 +22977,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
@@ -23061,6 +23065,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
@@ -23310,6 +23315,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CONTENT_PROTECTION,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
// These permissions may grant access to user data and therefore must be protected with
// MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls.
@@ -23975,6 +23982,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ @Override
+ public void setAppFunctionsPolicy(String callerPackageName, @AppFunctionsPolicy int policy) {
+ if (!android.app.appfunctions.flags.Flags.enableAppFunctionManager()) {
+ return;
+ }
+
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ int userId = caller.getUserId();
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APP_FUNCTIONS_POLICY);
+ EnforcingAdmin enforcingAdmin =
+ enforcePermissionAndGetEnforcingAdmin(
+ /* who */null, MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
+ callerPackageName, userId);
+
+ if (policy == APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.APP_FUNCTIONS, enforcingAdmin, userId);
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.APP_FUNCTIONS,
+ enforcingAdmin, new IntegerPolicyValue(policy),
+ userId);
+ }
+ }
+
+ @Override
+ public @AppFunctionsPolicy int getAppFunctionsPolicy(String callerPackageName, int userId) {
+ if (!android.app.appfunctions.flags.Flags.enableAppFunctionManager()) {
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforceCanQuery(MANAGE_DEVICE_POLICY_APP_FUNCTIONS, callerPackageName, userId);
+ Integer policy =
+ mDevicePolicyEngine.getResolvedPolicy(PolicyDefinition.APP_FUNCTIONS, userId);
+ if (policy == null) {
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+ return policy;
+ }
+
private void updateContentProtectionPolicyCache(@UserIdInt int userId) {
mPolicyCache.setContentProtectionPolicy(
userId,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 24b16b7c2c60..543e32fae55f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -318,6 +318,20 @@ final class PolicyDefinition<V> {
PolicyEnforcerCallbacks::setContentProtectionPolicy,
new IntegerPolicySerializer());
+ static PolicyDefinition<Integer> APP_FUNCTIONS = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.APP_FUNCTIONS_POLICY),
+ new MostRestrictive<>(
+ List.of(
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_DISABLED),
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_DISABLED_CROSS_PROFILE),
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY))),
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ PolicyEnforcerCallbacks::noOp,
+ new IntegerPolicySerializer());
+
static PolicyDefinition<Integer> PASSWORD_COMPLEXITY = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.PASSWORD_COMPLEXITY_POLICY),
new MostRestrictive<>(
@@ -398,6 +412,8 @@ final class PolicyDefinition<V> {
USB_DATA_SIGNALING);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY,
CONTENT_PROTECTION);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APP_FUNCTIONS_POLICY,
+ APP_FUNCTIONS);
// Intentionally not flagged since if the flag is flipped off on a device already
// having PASSWORD_COMPLEXITY policy in the on-device XML, it will cause the
// deserialization logic to break due to seeing an unknown tag.
@@ -656,10 +672,6 @@ final class PolicyDefinition<V> {
}
}
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- mPolicyKey.saveToXml(serializer);
- }
-
@Nullable
static <V> PolicyDefinition<V> readFromXml(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index a4fa0892da61..0d9dbaaec6b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -19,7 +19,6 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.PolicyValue;
-import android.app.admin.flags.Flags;
import android.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -41,7 +40,6 @@ final class PolicyState<V> {
private static final String TAG = "PolicyState";
private static final String TAG_ADMIN_POLICY_ENTRY = "admin-policy-entry";
- private static final String TAG_POLICY_DEFINITION_ENTRY = "policy-definition-entry";
private static final String TAG_RESOLVED_VALUE_ENTRY = "resolved-value-entry";
private static final String TAG_ENFORCING_ADMIN_ENTRY = "enforcing-admin-entry";
private static final String TAG_POLICY_VALUE_ENTRY = "policy-value-entry";
@@ -225,12 +223,6 @@ final class PolicyState<V> {
}
void saveToXml(TypedXmlSerializer serializer) throws IOException {
- if (!Flags.dontWritePolicyDefinition()) {
- serializer.startTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
- mPolicyDefinition.saveToXml(serializer);
- serializer.endTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
- }
-
if (mCurrentResolvedPolicy != null) {
serializer.startTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
mPolicyDefinition.savePolicyValueToXml(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aa63c4a4a91f..0eac4f2c922a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.ApplicationSharedMemory;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.pm.RoSystemFeatures;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.ProtoLog;
@@ -201,7 +202,6 @@ import com.android.server.net.watchlist.NetworkWatchlistService;
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
-import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
@@ -294,6 +294,7 @@ import com.android.server.usage.StorageStatsService;
import com.android.server.usage.UsageStatsService;
import com.android.server.usb.UsbService;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.vcn.VcnLocation;
import com.android.server.vibrator.VibratorManagerService;
import com.android.server.voiceinteraction.VoiceInteractionManagerService;
import com.android.server.vr.VrManagerService;
@@ -392,6 +393,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.sdksandbox.SdkSandboxManagerService$Lifecycle";
private static final String AD_SERVICES_MANAGER_SERVICE_CLASS =
"com.android.server.adservices.AdServicesManagerService$Lifecycle";
+ private static final String ON_DEVICE_INTELLIGENCE_MANAGER_SERVICE_CLASS =
+ "com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService";
private static final String ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS =
"com.android.server.ondevicepersonalization."
+ "OnDevicePersonalizationSystemService$Lifecycle";
@@ -1000,6 +1003,17 @@ public final class SystemServer implements Dumpable {
}
});
+ // Register callback to report native memory metrics post GC cleanup
+ // for system_server
+ if (android.app.Flags.reportPostgcMemoryMetrics() &&
+ com.android.libcore.readonly.Flags.postCleanupApis()) {
+ VMRuntime.addPostCleanupCallback(new Runnable() {
+ @Override public void run() {
+ MetricsLoggerWrapper.logPostGcMemorySnapshot();
+ }
+ });
+ }
+
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
@@ -1928,6 +1942,10 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
+ t.traceBegin("UpdateMetricsIfNeeded");
+ mPackageManagerService.updateMetricsIfNeeded();
+ t.traceEnd();
+
t.traceBegin("PerformFstrimIfNeeded");
try {
mPackageManagerService.performFstrimIfNeeded();
@@ -2233,10 +2251,13 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartVcnManagementService");
try {
- // TODO: b/375213246 When VCN is in mainline module, load it from the apex path.
- // Whether VCN will be in apex or in the platform will be gated by a build system
- // flag.
- mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
+ 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);
+ }
} catch (Throwable e) {
reportWtf("starting VCN Management Service", e);
}
@@ -2932,7 +2953,7 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
// UprobeStats DynamicInstrumentationManager
- if (com.android.art.flags.Flags.executableMethodFileOffsets()) {
+ if (android.uprobestats.flags.Flags.executableMethodFileOffsets()) {
t.traceBegin("StartDynamicInstrumentationManager");
mSystemServiceManager.startService(DynamicInstrumentationManagerService.class);
t.traceEnd();
@@ -3453,7 +3474,7 @@ public final class SystemServer implements Dumpable {
private void startOnDeviceIntelligenceService(TimingsTraceAndSlog t) {
t.traceBegin("startOnDeviceIntelligenceManagerService");
- mSystemServiceManager.startService(OnDeviceIntelligenceManagerService.class);
+ mSystemServiceManager.startService(ON_DEVICE_INTELLIGENCE_MANAGER_SERVICE_CLASS);
t.traceEnd();
}
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 4412968999e5..0d222fb4409e 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -10,14 +10,6 @@ flag {
}
flag {
- name: "remove_java_service_manager_cache"
- namespace: "system_performance"
- description: "This flag turns off Java's Service Manager caching mechanism."
- bug: "333854840"
- is_fixed_read_only: true
-}
-
-flag {
name: "remove_text_service"
namespace: "wear_frameworks"
description: "Remove TextServiceManagerService on Wear"
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index f5360eb9a56a..6b28047c2610 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -213,7 +213,8 @@ class ShareTargetPredictor extends AppTargetPredictor {
}
private int getShareEventType(IntentFilter intentFilter) {
- String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ String mimeType = (intentFilter != null && intentFilter.countDataTypes() > 0)
+ ? intentFilter.getDataType(0) : null;
return getDataManager().mimeTypeToShareEventType(mimeType);
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0b7438cd1b17..018cf20e914f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -464,6 +464,48 @@ class PermissionService(private val service: AccessCheckingService) :
return size
}
+ override fun getPermissionRequestState(
+ packageName: String,
+ permissionName: String,
+ deviceId: String
+ ): Int {
+ val uid = Binder.getCallingUid()
+ val result = context.checkPermission(permissionName, Binder.getCallingPid(), uid)
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ return Context.PERMISSION_REQUEST_STATE_GRANTED
+ }
+
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(uid, userId).use {
+ it.getPackageState(packageName)
+ } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ val androidPackage = packageState.androidPackage
+ ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ if (appId != packageState.appId) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+ val permission = service.getState {
+ with(policy) { getPermissions()[permissionName] }
+ }
+ if (permission == null || !permission.isRuntime) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+ if (permissionName !in androidPackage.requestedPermissions) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+
+ val permissionFlags = service.getState {
+ getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ }
+ return if (permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)) {
+ Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ } else {
+ Context.PERMISSION_REQUEST_STATE_REQUESTABLE
+ }
+ }
+
override fun checkUidPermission(uid: Int, permissionName: String, deviceId: String): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
@@ -472,7 +514,7 @@ class PermissionService(private val service: AccessCheckingService) :
// PackageManagerInternal.getPackage(int) already checks package visibility and enforces
// that instant apps can't see shared UIDs. Note that on the contrary,
- // Note that PackageManagerInternal.getPackage(String) doesn't perform any checks.
+ // PackageManagerInternal.getPackage(String) doesn't perform any checks.
val androidPackage = packageManagerInternal.getPackage(uid)
if (androidPackage != null) {
// Note that PackageManagerInternal.getPackageStateInternal() is not filtered.
diff --git a/services/proguard.flags b/services/proguard.flags
index 977bd19a7236..0e1f68e03d7d 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -44,6 +44,9 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+# allow invoking start-service using class name in both apex and services jar.
+-keep,allowoptimization,allowaccessmodification class com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService { *; }
+
# Keep all aconfig Flag class as they might be statically referenced by other packages
# An merge or inlining could lead to missing dependencies that cause run time errors
-keepclassmembernames class android.**.Flags, com.android.**.Flags { public *; }
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 3093c424e8b2..073ee31ddd60 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -16,6 +16,11 @@
package com.android.server.supervision;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.internal.util.Preconditions.checkCallAuthorization;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -29,11 +34,14 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.os.Binder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.util.SparseArray;
import com.android.internal.R;
@@ -63,17 +71,22 @@ public class SupervisionService extends ISupervisionManager.Stub {
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
private final DevicePolicyManagerInternal mDpmInternal;
+ private final PackageManager mPackageManager;
private final UserManagerInternal mUserManagerInternal;
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+ mPackageManager = context.getPackageManager();
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
+ if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+ enforcePermission(INTERACT_ACROSS_USERS);
+ }
synchronized (getLockObject()) {
return getUserDataLocked(userId).supervisionEnabled;
}
@@ -147,13 +160,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
/** Returns whether the supervision app has profile owner status. */
private boolean isProfileOwner(@UserIdInt int userId) {
- ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(userId);
- if (profileOwner == null) {
- return false;
- }
+ ComponentName profileOwner =
+ mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
+ return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+ }
+
+ /** Returns whether the given package name belongs to the supervision role holder. */
+ private boolean isSupervisionAppPackage(String packageName) {
+ return packageName.equals(
+ mContext.getResources().getString(R.string.config_systemSupervision));
+ }
- String configPackage = mContext.getResources().getString(R.string.config_systemSupervision);
- return profileOwner.getPackageName().equals(configPackage);
+ /** Enforces that the caller has the given permission. */
+ private void enforcePermission(String permission) {
+ checkCallAuthorization(
+ mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED);
}
public static class Lifecycle extends SystemService {
@@ -211,6 +232,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
@Override
+ public boolean isActiveSupervisionApp(int uid) {
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages == null) {
+ return false;
+ }
+ for (var packageName : packages) {
+ if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
+ int userId = UserHandle.getUserId(uid);
+ return SupervisionService.this.isSupervisionEnabledForUser(userId);
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
return SupervisionService.this.isSupervisionEnabledForUser(userId);
}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
index 5492ba6b9dd1..6e14bad11837 100644
--- a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -37,7 +38,7 @@ import java.lang.reflect.Method;
/**
* Test class for
- * {@link DynamicInstrumentationManagerService#parseMethodDescriptor(ClassLoader,
+ * {@link MethodDescriptorParser#parseMethodDescriptor(ClassLoader,
* MethodDescriptor)}.
* <p>
* Build/Install/Run:
@@ -119,13 +120,13 @@ public class ParseMethodDescriptorTest {
}
private Method parseMethodDescriptor(String fqcn, String methodName) {
- return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ return MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(),
getMethodDescriptor(fqcn, methodName, new String[]{}));
}
private Method parseMethodDescriptor(String fqcn, String methodName, String[] fqParameters) {
- return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ return MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(),
getMethodDescriptor(fqcn, methodName, fqParameters));
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
index acd34e323dc2..0ae7699aeb71 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
@@ -233,6 +233,6 @@ public class BroadcastHelperTest {
mBroadcastHelper.sendPackageChangedBroadcast(mMockSnapshot,
PACKAGE_CHANGED_TEST_PACKAGE_NAME, true /* dontKillApp */, componentNames,
- UserHandle.USER_SYSTEM, "test" /* reason */);
+ UserHandle.USER_SYSTEM, "test" /* reason */, "test" /* reasonForTrace */);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
index f589a2c9385c..7db6ea0bf86d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
@@ -60,12 +60,6 @@ class BrightnessRangeControllerTest {
}
@Test
- fun testMaxBrightness_HbmDisabledAndNotAllowed() {
- val controller = createController(nbmEnabled = false, hbmAllowed = false)
- assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
- }
-
- @Test
fun testMaxBrightness_transitionPointLessThanCurrentNbmLimit() {
val controller = createController(
hbmAllowed = false,
@@ -76,13 +70,11 @@ class BrightnessRangeControllerTest {
}
private fun createController(
- nbmEnabled: Boolean = true,
hbmSupported: Boolean = true,
hbmAllowed: Boolean = true,
hbmMaxBrightness: Float = MAX_BRIGHTNESS,
nbmMaxBrightness: Float = NORMAL_BRIGHTNESS_LOW
): BrightnessRangeController {
- whenever(mockFlags.isNbmControllerEnabled).thenReturn(nbmEnabled)
whenever(mockHbmController.deviceSupportsHbm()).thenReturn(hbmSupported)
whenever(mockHbmController.isHbmCurrentlyAllowed).thenReturn(hbmAllowed)
whenever(mockHbmController.currentBrightnessMax).thenReturn(hbmMaxBrightness)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 365cbaed2aac..f96294ed4ca8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -30,6 +30,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_D
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
+import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
@@ -88,6 +89,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.Sensor;
@@ -3708,7 +3710,7 @@ public class DisplayManagerServiceTest {
eq(config));
bs.releaseVirtualDisplay(mMockAppToken);
- verify(mMockVirtualDisplayAdapter).releaseVirtualDisplayLocked(binder, callingUid);
+ verify(mMockVirtualDisplayAdapter).releaseVirtualDisplayLocked(binder);
}
@Test
@@ -3830,6 +3832,96 @@ public class DisplayManagerServiceTest {
assertThat(callback.receivedEvents()).isEmpty();
}
+ @Test
+ public void testMirrorBuiltInDisplay_flagEnabled() {
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.systemReady(/* safeMode= */ false);
+ assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+ final ContentObserver observer = displayManager.getSettingsObserver();
+ observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+ assertThat(displayManager.shouldMirrorBuiltInDisplay()).isTrue();
+ }
+
+ @Test
+ public void testMirrorBuiltInDisplay_flagDisabled() {
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(false);
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.systemReady(/* safeMode= */ false);
+ assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+ final ContentObserver observer = displayManager.getSettingsObserver();
+ observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+ assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+ }
+
+ @Test
+ public void testShouldNotNotifyDefaultDisplayChanges_whenMirrorBuiltInDisplayChanges() {
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.systemReady(/* safeMode= */ false);
+
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ displayManagerBinderService.registerCallbackWithEventMask(
+ callback, STANDARD_DISPLAY_EVENTS);
+ waitForIdleHandler(handler);
+
+ // Create a default display device
+ createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_INTERNAL);
+
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+ final ContentObserver observer = displayManager.getSettingsObserver();
+ observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+ waitForIdleHandler(handler);
+
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ }
+
+ @Test
+ public void testShouldNotifyNonDefaultDisplayChanges_whenMirrorBuiltInDisplayChanges() {
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.systemReady(/* safeMode= */ false);
+
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ displayManagerBinderService.registerCallbackWithEventMask(
+ callback, STANDARD_DISPLAY_EVENTS);
+ waitForIdleHandler(handler);
+
+ // Create a default display device
+ createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_INTERNAL);
+ // Create a non-default display device
+ createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_EXTERNAL);
+
+ Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+ final ContentObserver observer = displayManager.getSettingsObserver();
+ observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+ waitForIdleHandler(handler);
+
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 91f1aaf603e6..a4dfecb8ed96 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2471,6 +2471,26 @@ public final class DisplayPowerControllerTest {
eq(false));
}
+ @Test
+ public void onDisplayChange_canceledAfterStop() {
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+
+ // stop the dpc (turn it down)
+ mHolder.dpc.stop();
+ advanceTime(1);
+
+ // To trigger all the changes that can happen, we will completely change the underlying
+ // display device.
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
+
+ // Call onDisplayChange after we stopped DPC and make sure it doesn't crash
+ mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
+ advanceTime(1);
+
+ // No crash = success
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -2606,8 +2626,8 @@ public final class DisplayPowerControllerTest {
mock(ScreenOffBrightnessSensorController.class);
final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
final HdrClamper hdrClamper = mock(HdrClamper.class);
- final NormalBrightnessModeController normalBrightnessModeController = mock(
- NormalBrightnessModeController.class);
+ final NormalBrightnessModeController normalBrightnessModeController =
+ new NormalBrightnessModeController();
BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 241dc10747ac..1a0ab252f128 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -609,4 +609,69 @@ public class LogicalDisplayTest {
DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked();
assertArrayEquals(appSupportedModes, info.appsSupportedModes);
}
+
+ @Test
+ public void testSetCanHostTasks_defaultDisplay() {
+ mLogicalDisplay = new LogicalDisplay(Display.DEFAULT_DISPLAY, LAYER_STACK, mDisplayDevice);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(true);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(false);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+ }
+
+ @Test
+ public void testSetCanHostTasks_nonDefaultNormalDisplay() {
+ mLogicalDisplay =
+ new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+
+ mLogicalDisplay.setCanHostTasksLocked(true);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(false);
+ assertFalse(mLogicalDisplay.canHostTasksLocked());
+ }
+
+ @Test
+ public void testSetCanHostTasks_nonDefaultVirtualMirrorDisplay() {
+ mDisplayDeviceInfo.type = Display.TYPE_VIRTUAL;
+ when(mDisplayDevice.shouldOnlyMirror()).thenReturn(true);
+ mLogicalDisplay =
+ new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+ mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager);
+
+ mLogicalDisplay.setCanHostTasksLocked(true);
+ assertFalse(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(false);
+ assertFalse(mLogicalDisplay.canHostTasksLocked());
+ }
+
+ @Test
+ public void testSetCanHostTasks_nonDefaultRearDisplay() {
+ mLogicalDisplay =
+ new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+ mLogicalDisplay.setDevicePositionLocked(Layout.Display.POSITION_REAR);
+
+ mLogicalDisplay.setCanHostTasksLocked(true);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(false);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+ }
+
+ @Test
+ public void testSetCanHostTasks_nonDefaultOwnContentOnly() {
+ mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+ mLogicalDisplay =
+ new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+
+ mLogicalDisplay.setCanHostTasksLocked(true);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+ mLogicalDisplay.setCanHostTasksLocked(false);
+ assertTrue(mLogicalDisplay.canHostTasksLocked());
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index dbd5c65f9ba3..9287b3004279 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -118,14 +118,13 @@ public class VirtualDisplayAdapterTest {
public void testCreateAndReleaseVirtualDisplay() {
VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
- int ownerUid = 10;
DisplayDevice result = mAdapter.createVirtualDisplayLocked(mMockCallback,
- /* projection= */ null, ownerUid, /* packageName= */ "testpackage",
+ /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
/* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config);
assertNotNull(result);
- result = mAdapter.releaseVirtualDisplayLocked(mMockBinder, ownerUid);
+ result = mAdapter.releaseVirtualDisplayLocked(mMockBinder);
assertNotNull(result);
}
@@ -230,7 +229,6 @@ public class VirtualDisplayAdapterTest {
// Displays for the same package
for (int i = 0; i < MAX_DEVICES_PER_PACKAGE * 2; i++) {
- // Same owner UID
IVirtualDisplayCallback callback = createCallback();
DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
mMediaProjectionMock, 1234, "test.package", "123",
@@ -240,7 +238,6 @@ public class VirtualDisplayAdapterTest {
// Displays for different packages
for (int i = 0; i < MAX_DEVICES * 2; i++) {
- // Same owner UID
IVirtualDisplayCallback callback = createCallback();
DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
mMediaProjectionMock, 1234 + i, "test.package", "123",
@@ -270,8 +267,7 @@ public class VirtualDisplayAdapterTest {
}
// Release one display
- DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder(),
- ownerUid);
+ DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder());
assertNotNull(device);
callbacks.remove(0);
@@ -292,7 +288,7 @@ public class VirtualDisplayAdapterTest {
// Release all the displays
for (IVirtualDisplayCallback cb : callbacks) {
- device = mAdapter.releaseVirtualDisplayLocked(cb.asBinder(), ownerUid);
+ device = mAdapter.releaseVirtualDisplayLocked(cb.asBinder());
assertNotNull(device);
}
callbacks.clear();
@@ -342,8 +338,7 @@ public class VirtualDisplayAdapterTest {
}
// Release one display
- DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder(),
- firstOwnerUid);
+ DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder());
assertNotNull(device);
callbacks.remove(0);
@@ -363,9 +358,8 @@ public class VirtualDisplayAdapterTest {
assertNull(device);
// Release all the displays
- for (int i = 0; i < callbacks.size(); i++) {
- device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(i).asBinder(),
- firstOwnerUid + i);
+ for (IVirtualDisplayCallback iVirtualDisplayCallback : callbacks) {
+ device = mAdapter.releaseVirtualDisplayLocked(iVirtualDisplayCallback.asBinder());
assertNotNull(device);
}
callbacks.clear();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index df09b046ddd2..6d1e56d1f479 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -68,6 +68,8 @@ public final class BrightnessEventTest {
mBrightnessEvent.setAutomaticBrightnessEnabled(true);
mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME);
mBrightnessEvent.setAutoBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mBrightnessEvent.setSlowChange(true);
+ mBrightnessEvent.setRampSpeed(0.3f);
}
@Test
@@ -88,7 +90,7 @@ public final class BrightnessEventTest {
+ "preLux=150.0, wasShortTermModelActive=true, autoBrightness=true (idle), "
+ "unclampedBrt=0.65, hbmMax=0.62, hbmMode=off, thrmMax=0.65, "
+ "rbcStrength=-1, powerFactor=0.2, physDisp=display_name(987654321), "
- + "logicalId=1";
+ + "logicalId=1, slowChange=true, rampSpeed=0.3";
assertEquals(expectedString, actualString);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 66e9c98b6481..238654d2aaf1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -126,16 +127,11 @@ public class BrightnessClamperControllerTest {
@Test
public void testConstructor_doesNotStartsLightSensorController() {
- verify(mMockLightSensorController, never()).restart();
- }
-
- @Test
- public void testConstructor_startsLightSensorController() {
when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
mClamperController = createBrightnessClamperController();
- verify(mMockLightSensorController).restart();
+ verify(mMockLightSensorController, never()).restart();
}
@Test
@@ -171,20 +167,43 @@ public class BrightnessClamperControllerTest {
@Test
public void testOnDisplayChanged_doesNotRestartLightSensor() {
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_ON);
+ reset(mMockLightSensorController);
+
mClamperController.onDisplayChanged(mMockDisplayDeviceData);
verify(mMockLightSensorController, never()).restart();
+ verify(mMockLightSensorController).stop();
}
@Test
public void testOnDisplayChanged_restartsLightSensor() {
when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_ON);
+ reset(mMockLightSensorController);
+
mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+ verify(mMockLightSensorController, never()).stop();
verify(mMockLightSensorController).restart();
}
@Test
+ public void testOnDisplayChanged_doesNotRestartLightSensor_screenOff() {
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_OFF);
+ reset(mMockLightSensorController);
+
+ mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+
+ verify(mMockLightSensorController, never()).restart();
+ verify(mMockLightSensorController).stop();
+ }
+
+ @Test
public void testClamp_AppliesModifier() {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index f391e409a717..4e81b3530b62 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -20,10 +20,13 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -89,6 +92,7 @@ public class ColorDisplayServiceTest {
private ColorDisplayService.BinderService mBinderService;
private Resources mResourcesSpy;
+ private ReduceBrightColorsTintController mRbcSpy;
private static final int[] MINIMAL_COLOR_MODES = new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -135,7 +139,8 @@ public class ColorDisplayServiceTest {
mLocalServiceKeeperRule.overrideLocalService(
DisplayManagerInternal.class, mDisplayManagerInternal);
- mCds = new ColorDisplayService(mContext);
+ mRbcSpy = Mockito.spy(new ReduceBrightColorsTintController());
+ mCds = new ColorDisplayService(mContext, mRbcSpy);
mBinderService = mCds.new BinderService();
mLocalServiceKeeperRule.overrideLocalService(
ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -1106,7 +1111,8 @@ public class ColorDisplayServiceTest {
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
- eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+ eq(Display.COLOR_MODE_INVALID));
}
@Test
@@ -1124,7 +1130,8 @@ public class ColorDisplayServiceTest {
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
- eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+ eq(Display.COLOR_MODE_INVALID));
}
@Test
@@ -1140,7 +1147,8 @@ public class ColorDisplayServiceTest {
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
- eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+ eq(Display.COLOR_MODE_SRGB));
}
@Test
@@ -1156,7 +1164,8 @@ public class ColorDisplayServiceTest {
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
verify(mDisplayTransformManager).setColorMode(
- eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
+ eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), any(),
+ eq(Display.COLOR_MODE_INVALID));
}
@Test
@@ -1164,10 +1173,27 @@ public class ColorDisplayServiceTest {
when(mResourcesSpy.getIntArray(R.array.config_availableColorModes))
.thenReturn(new int[] {});
startService();
- verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt());
+ verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), any(), anyInt());
assertThat(mBinderService.getColorMode()).isEqualTo(-1);
}
+ @Test
+ public void ensureColorModeChangeTriggersRbcReload() {
+ // should set up RBC once at startup
+ startService();
+ reset(mRbcSpy);
+
+ // Make sure RBC is enabled and available for this test
+ doReturn(true).when(mRbcSpy).isAvailable(mContext);
+
+ // When Color Mode changes, RBC needs to re-setup
+ // onDisplayColorModeChanged cancels animations, and should be called in handler thread
+ mCds.mHandler.runWithScissors(
+ () -> mCds.onDisplayColorModeChanged(ColorDisplayManager.COLOR_MODE_NATURAL),
+ 1000);
+ verify(mRbcSpy, times(1)).setUp(eq(mContext), anyBoolean());
+ }
+
/**
* Configures Night display to use a custom schedule.
*
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
index 27f87aae35bb..a7ef5e0afc0e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
@@ -19,6 +19,7 @@ package com.android.server.display.color;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS;
import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE;
import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR;
import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION;
@@ -51,12 +52,14 @@ public class DisplayTransformManagerTest {
private MockitoSession mSession;
private DisplayTransformManager mDtm;
private float[] mNightDisplayMatrix;
+ private float[] mRbcMatrix;
private HashMap<String, String> mSystemProperties;
@Before
public void setUp() {
mDtm = new DisplayTransformManager();
mNightDisplayMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
+ mRbcMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS);
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
@@ -81,7 +84,8 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_natural() {
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
+ Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo("0" /* managed */);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -90,7 +94,8 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_boosted() {
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, mRbcMatrix,
+ Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo("0" /* managed */);
@@ -100,7 +105,8 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_saturated() {
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, mRbcMatrix,
+ Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo("1" /* unmanaged */);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -109,7 +115,8 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_automatic() {
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, mRbcMatrix,
+ Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo("2" /* enhanced */);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -118,7 +125,7 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_vendor() {
- mDtm.setColorMode(0x100, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(0x100, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo(Integer.toString(0x100) /* pass-through */);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -127,7 +134,7 @@ public class DisplayTransformManagerTest {
@Test
public void setColorMode_outOfBounds() {
- mDtm.setColorMode(0x50, mNightDisplayMatrix, -1);
+ mDtm.setColorMode(0x50, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
.isEqualTo(null);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -141,7 +148,7 @@ public class DisplayTransformManagerTest {
// Start with a known state, which we expect to remain unmodified
SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue);
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix,
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
Display.COLOR_MODE_INVALID);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE))
.isEqualTo(magicPropertyValue);
@@ -155,7 +162,7 @@ public class DisplayTransformManagerTest {
// Start with a known state, which we expect to get modified
SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue);
- mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix,
+ mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
testPropertyValue);
assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE))
.isEqualTo(Integer.toString(testPropertyValue));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RejectedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RejectedModesVoteTest.kt
new file mode 100644
index 000000000000..dd3211d0ef83
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RejectedModesVoteTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.server.display.mode
+
+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.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RejectedModesVoteTest {
+ private val rejectedModes = setOf(1, 2)
+
+ private val otherMode = 2
+
+ private lateinit var rejectedModesVote: RejectedModesVote
+
+ @Before
+ fun setUp() {
+ rejectedModesVote = RejectedModesVote(rejectedModes)
+ }
+
+ @Test
+ fun addsRejectedModeIds_summaryIsEmpty() {
+ val summary = createVotesSummary()
+
+ rejectedModesVote.updateSummary(summary)
+
+ assertThat(summary.rejectedModeIds).containsExactlyElementsIn(rejectedModes)
+ }
+
+ @Test
+ fun addsRejectedModeIds_summaryIsNotEmpty() {
+ val summary = createVotesSummary()
+ summary.rejectedModeIds.add(otherMode)
+
+ rejectedModesVote.updateSummary(summary)
+
+ assertThat(summary.rejectedModeIds).containsExactlyElementsIn(rejectedModes + otherMode)
+ }
+} \ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index 239e59b69187..958cf21a38a2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -186,6 +186,44 @@ class VoteSummaryTest {
assertThat(result).hasSize(1)
}
+
+ enum class RejectedModesTestCase(
+ internal val summaryRejectedModes: Set<Int>?,
+ val modesToFilter: Array<Display.Mode>,
+ val expectedModeIds: Set<Int>
+ ) {
+ HAS_NO_MATCHING_VOTE(
+ setOf(4, 5),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ setOf(1, 2, 3)
+ ),
+ HAS_SINGLE_MATCHING_VOTE(
+ setOf(1),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ setOf(2, 3)
+ ),
+ HAS_MULTIPLE_MATCHING_VOTES(
+ setOf(1, 2),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ setOf(3)
+ ),
+ }
+
+ @Test
+ fun testFilterModes_rejectedModes(@TestParameter testCase: RejectedModesTestCase) {
+ val summary = createSummary()
+ summary.rejectedModeIds = testCase.summaryRejectedModes
+
+ val result = summary.filterModes(testCase.modesToFilter)
+
+ assertThat(result.map {it.modeId}).containsExactlyElementsIn(testCase.expectedModeIds)
+ }
}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
index 874e99173c63..495e853370ee 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
@@ -46,7 +46,6 @@ import android.os.RemoteException;
import android.os.test.TestLooper;
import android.service.dreams.IDreamService;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -108,10 +107,8 @@ public class DreamControllerTest {
.thenReturn(Context.ACTIVITY_TASK_SERVICE);
final PowerManager powerManager = new PowerManager(mContext, mPowerManager, null, null);
- when(mContext.getSystemService(Context.POWER_SERVICE))
+ when(mContext.getSystemService(PowerManager.class))
.thenReturn(powerManager);
- when(mContext.getSystemServiceName(PowerManager.class))
- .thenReturn(Context.POWER_SERVICE);
when(mContext.getResources()).thenReturn(mResources);
mToken = new Binder();
@@ -234,8 +231,13 @@ public class DreamControllerTest {
}
@Test
- @FlakyTest(bugId = 293109503)
public void serviceDisconnect_resetsScreenTimeout() throws RemoteException {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_resetScreenTimeoutOnUnexpectedDreamExit))
+ .thenReturn(true);
+ // Recreate DreamManager because the configuration gets retrieved in the constructor
+ mDreamController = new DreamController(mContext, mHandler, mListener);
+
// Start dream.
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
@@ -254,8 +256,13 @@ public class DreamControllerTest {
}
@Test
- @FlakyTest(bugId = 293109503)
public void binderDied_resetsScreenTimeout() throws RemoteException {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_resetScreenTimeoutOnUnexpectedDreamExit))
+ .thenReturn(true);
+ // Recreate DreamManager because the configuration gets retrieved in the constructor
+ mDreamController = new DreamController(mContext, mHandler, mListener);
+
// Start dream.
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
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 1efe4707fc11..9e96800ca2e9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
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_CACHED_ACTIVITY;
@@ -40,6 +41,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -283,6 +285,15 @@ public class MockingOomAdjusterTests {
}
}
+ private static void assertNoCpuTime(ProcessRecord app) {
+ assertEquals(0, app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME);
+ }
+
+ private static void assertCpuTime(ProcessRecord app) {
+ assertEquals(PROCESS_CAPABILITY_CPU_TIME,
+ app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME);
+ }
+
private static void assertBfsl(ProcessRecord app) {
assertEquals(PROCESS_CAPABILITY_BFSL,
app.mState.getSetCapability() & PROCESS_CAPABILITY_BFSL);
@@ -661,6 +672,7 @@ public class MockingOomAdjusterTests {
// SHORT_SERVICE, timed out already.
s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
+
mProcessStateController.setStartRequested(s, true);
s.isForeground = true;
s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -687,6 +699,51 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+ public void testUpdateOomAdjFreezeState_bindingFromShortFgs() {
+ // Setting up a started short FGS within app1.
+ final ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
+ s.appInfo = new ApplicationInfo();
+ mProcessStateController.setStartRequested(s, true);
+ s.isForeground = true;
+ s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
+ mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
+
+ final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ mProcessStateController.setHostProcess(s, app);
+ mProcessStateController.setHasForegroundServices(app.mServices, true,
+ FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
+ mProcessStateController.startService(app.mServices, s);
+ app.mState.setLastTopTime(SystemClock.uptimeMillis()
+ - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+
+ final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ // App1 with short service binds to app2
+ bindService(app2, app, null, null, 0, mock(IBinder.class));
+
+ setProcessesToLru(app, app2);
+ updateOomAdj(app);
+
+ assertCpuTime(app);
+ assertCpuTime(app2);
+
+ // Timeout the short FGS.
+ mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis()
+ - mService.mConstants.mShortFgsTimeoutDuration
+ - mService.mConstants.mShortFgsProcStateExtraWaitDuration);
+ mService.mServices.onShortFgsProcstateTimeout(s);
+ // mService is a mock, but this verifies that the timeout would trigger an update.
+ verify(mService).updateOomAdjLocked(app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT);
+ updateOomAdj(app);
+
+ assertNoCpuTime(app);
+ assertNoCpuTime(app2);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_OverlayUi() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -3142,11 +3199,19 @@ public class MockingOomAdjusterTests {
assertEquals(true, app.getUidRecord().isSetAllowListed());
assertFreezeState(app, false);
assertFreezeState(app2, false);
+ if (Flags.useCpuTimeCapability()) {
+ assertCpuTime(app);
+ assertCpuTime(app2);
+ }
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertFreezeState(app, true);
assertFreezeState(app2, true);
+ if (Flags.useCpuTimeCapability()) {
+ assertNoCpuTime(app);
+ assertNoCpuTime(app2);
+ }
}
@SuppressWarnings("GuardedBy")
@@ -3171,6 +3236,11 @@ public class MockingOomAdjusterTests {
assertFreezeState(app, false);
assertFreezeState(app2, false);
assertFreezeState(app3, false);
+ if (Flags.useCpuTimeCapability()) {
+ assertCpuTime(app);
+ assertCpuTime(app2);
+ assertCpuTime(app3);
+ }
// Remove app1 from allowlist.
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
@@ -3179,6 +3249,11 @@ public class MockingOomAdjusterTests {
assertFreezeState(app, true);
assertFreezeState(app2, false);
assertFreezeState(app3, false);
+ if (Flags.useCpuTimeCapability()) {
+ assertNoCpuTime(app);
+ assertCpuTime(app2);
+ assertCpuTime(app3);
+ }
// Now remove app2 from allowlist.
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
@@ -3187,6 +3262,11 @@ public class MockingOomAdjusterTests {
assertFreezeState(app, true);
assertFreezeState(app2, true);
assertFreezeState(app3, true);
+ if (Flags.useCpuTimeCapability()) {
+ assertNoCpuTime(app);
+ assertNoCpuTime(app2);
+ assertNoCpuTime(app3);
+ }
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 2988c77703b7..7e052dcba3fd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
@@ -64,6 +65,8 @@ import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -326,6 +329,7 @@ public final class ServiceBindingOomAdjPolicyTest {
@Test
@RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+ @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
public void testServiceDistinctBindingOomAdjShouldNotFreeze() throws Exception {
// Enable the flags.
mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -418,6 +422,7 @@ public final class ServiceBindingOomAdjPolicyTest {
@Test
@RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+ @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
public void testServiceDistinctBindingOomAdjAllowOomManagement() throws Exception {
// Enable the flags.
mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -497,6 +502,7 @@ public final class ServiceBindingOomAdjPolicyTest {
@Test
@RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+ @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
public void testServiceDistinctBindingOomAdjWaivePriority_propagateUnfreeze() throws Exception {
// Enable the flags.
mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -574,6 +580,50 @@ public final class ServiceBindingOomAdjPolicyTest {
}
@Test
+ @RequiresFlagsEnabled({
+ Flags.FLAG_UNFREEZE_BIND_POLICY_FIX,
+ Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY
+ })
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+ public void testServiceDistinctBindingOomAdj_propagateCpuTimeCapability() throws Exception {
+ // Note that PROCESS_CAPABILITY_CPU_TIME is special and should be propagated even when
+ // BIND_INCLUDE_CAPABILITIES is not present.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP1_NAME,
+ this::setHomeProcess,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+ PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ this::setHasForegroundServices,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+
+ // BIND_WAIVE_PRIORITY should not affect propagation of capability CPU_TIME
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME,
+ this::setHasForegroundServices,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, HOME_APP_ADJ,
+ PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ this::setHomeProcess,
+ BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+ atLeastOnce(), atLeastOnce());
+
+ // If both process have the capability, the bind should not need an update but the unbind
+ // is not safe to skip.
+ // Note that this check can fail on future changes that are not related to
+ // PROCESS_CAPABILITY_CPU_TIME and trigger updates but this is important to ensure
+ // efficiency of OomAdjuster.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP1_NAME,
+ this::setHomeProcess,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, HOME_APP_ADJ,
+ PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ this::setHomeProcess,
+ BIND_AUTO_CREATE,
+ never(), atLeastOnce());
+ }
+
+ @Test
@RequiresFlagsDisabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception {
// Enable the flags.
@@ -624,6 +674,9 @@ public final class ServiceBindingOomAdjPolicyTest {
// Enable the flags.
mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+ // Note that some capabilities like PROCESS_CAPABILITY_CPU_TIME are special and propagated
+ // regardless of BIND_INCLUDE_CAPABILITIES. We don't test for them here.
+
// Verify that there should be 0 oom adj update
// because we didn't specify the "BIND_INCLUDE_CAPABILITIES"
performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
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 c831475577d8..1e7a4f6cf51b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -83,6 +83,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -2319,11 +2320,12 @@ public class JobSchedulerServiceTest {
}
/**
- * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling
+ * Tests that jobs scheduled through a proxy (eg. system server) count towards scheduling
* limits.
*/
@Test
- public void testScheduleLimiting_Proxy() {
+ @DisableFlags(Flags.FLAG_ENFORCE_SCHEDULE_LIMIT_TO_PROXY_JOBS)
+ public void testScheduleLimiting_Proxy_NotCountTowardsLimit() {
mService.mConstants.ENABLE_API_QUOTAS = true;
mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
@@ -2342,6 +2344,32 @@ public class JobSchedulerServiceTest {
}
/**
+ * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling
+ * limits.
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_ENFORCE_SCHEDULE_LIMIT_TO_PROXY_JOBS)
+ public void testScheduleLimiting_Proxy_CountTowardsLimit() {
+ mService.mConstants.ENABLE_API_QUOTAS = true;
+ mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+ mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true;
+ mService.updateQuotaTracker();
+ mService.resetScheduleQuota();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ for (int i = 0; i < 500; ++i) {
+ final int expected =
+ i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ expected,
+ mService.scheduleAsPackage(job, null, TEST_UID, "proxied.package", 0, "JSSTest",
+ ""));
+ }
+ }
+
+ /**
* Tests that jobs scheduled by an app for itself as if through a proxy are counted towards
* scheduling limits.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
index 6b7eda26b945..c89048a06ce2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
@@ -280,6 +280,51 @@ public class LocationFudgerCacheTest {
eq(POINT_IN_TIMES_SQUARE[1]), eq(numAdditionalCells), any());
}
+ @Test
+ public void fetchDefaultCoarseningLevelIfNeeded_withDefaultValue_doesNotQueryProvider()
+ throws RemoteException {
+ // Arrange.
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onResult(10);
+
+ assertThat(cache.hasDefaultValue()).isTrue();
+
+ // Act.
+ cache.fetchDefaultCoarseningLevelIfNeeded();
+
+ // Assert. The method is not called again.
+ verify(provider, times(1)).getDefaultCoarseningLevel(any());
+ }
+
+ @Test
+ public void fetchDefaultCoarseningLevelIfNeeded_withoutDefaultValue_doesQueryProvider()
+ throws RemoteException {
+ // Arrange.
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onError();
+
+ assertThat(cache.hasDefaultValue()).isFalse();
+
+ // Act.
+ cache.fetchDefaultCoarseningLevelIfNeeded();
+
+ // Assert. The method is called again.
+ verify(provider, times(2)).getDefaultCoarseningLevel(any());
+ }
@Test
public void locationFudgerCache_canContainUpToMaxSizeItems() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index d58e772f991d..2e4652e6f48b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -176,8 +176,12 @@ public class LocationFudgerTest {
}
@Test
- public void testDensityBasedCoarsening_ifFeatureIsDisabled_cacheIsNotUsed() {
+ public void testDensityBasedCoarsening_ifAnyFlagIsOff1_cacheIsNotUsed() {
+ // This feature requires two flags: one for the population density provider (which could
+ // be used by various client), and a second one for actually enabling the new coarsening
+ // algorithm.
mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
LocationFudgerCache cache = mock(LocationFudgerCache.class);
mFudger.setLocationFudgerCache(cache);
@@ -188,8 +192,25 @@ public class LocationFudgerTest {
}
@Test
- public void testDensityBasedCoarsening_ifFeatureIsEnabledButNotDefault_cacheIsNotUsed() {
- mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ public void testDensityBasedCoarsening_ifAnyFlagIsOff2_cacheIsNotUsed() {
+ // This feature requires two flags: one for the population density provider (which could
+ // be used by various client), and a second one for actually enabling the new coarsening
+ // algorithm.
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
+ LocationFudgerCache cache = mock(LocationFudgerCache.class);
+
+ mFudger.setLocationFudgerCache(cache);
+
+ mFudger.createCoarse(createLocation("test", mRandom));
+
+ verify(cache, never()).getCoarseningLevel(anyDouble(), anyDouble());
+ }
+
+ @Test
+ public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_cacheIsNotUsed() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
LocationFudgerCache cache = mock(LocationFudgerCache.class);
doReturn(false).when(cache).hasDefaultValue();
@@ -201,8 +222,23 @@ public class LocationFudgerTest {
}
@Test
+ public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_defaultIsFetched() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
+ LocationFudgerCache cache = mock(LocationFudgerCache.class);
+ doReturn(false).when(cache).hasDefaultValue();
+
+ mFudger.setLocationFudgerCache(cache);
+
+ mFudger.createCoarse(createLocation("test", mRandom));
+
+ verify(cache).fetchDefaultCoarseningLevelIfNeeded();
+ }
+
+ @Test
public void testDensityBasedCoarsening_ifFeatureIsEnabledAndDefaultIsSet_cacheIsUsed() {
mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
LocationFudgerCache cache = mock(LocationFudgerCache.class);
doReturn(true).when(cache).hasDefaultValue();
@@ -223,6 +259,7 @@ public class LocationFudgerTest {
// location/geometry/S2CellIdUtilsTest.java
mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
// Arbitrary location in Times Square, NYC
double[] latLng = new double[] {40.758896, -73.985130};
int s2Level = 1;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index de029e0d770f..90e1263b76ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -56,8 +56,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
- pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(), pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
val modifiedPackages = pkgListCaptor.value
val distractionFlags = flagsCaptor.value
@@ -158,8 +158,7 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper).sendDistractingPackagesChanged(
- any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID),
- flagsCaptor.capture())
+ any(), pkgListCaptor.capture(), any(), eq(TEST_USER_ID), flagsCaptor.capture())
val modifiedPackages = pkgListCaptor.value
val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -198,12 +197,12 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
@Test
fun sendDistractingPackagesChanged() {
- broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+ broadcastHelper.sendDistractingPackagesChanged(pms::snapshotComputer,
packagesToChange, uidsToChange, TEST_USER_ID,
PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
testHandler.flush()
- verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
- pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(), pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
var changedPackages = pkgListCaptor.value
var changedUids = uidsCaptor.value
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
index 0304a74f7654..cd8d415bdfa2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
@@ -21,11 +21,7 @@ import static android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.SharedLibraryInfo;
@@ -90,32 +86,15 @@ public class InstallDependencyHelperTest {
}
@Test
- public void testResolveLibraryDependenciesIfNeeded_errorInSharedLibrariesImpl()
- throws Exception {
- doThrow(new PackageManagerException(new Exception("xyz")))
- .when(mSharedLibraries).collectMissingSharedLibraryInfos(any());
-
- PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
- CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
- 0, mHandler, callback);
- callback.assertFailure();
-
- assertThat(callback.error).hasMessageThat().contains("xyz");
- }
-
- @Test
public void testResolveLibraryDependenciesIfNeeded_failsToBind() throws Exception {
// Return a non-empty list as missing dependency
PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
List<SharedLibraryInfo> missingDependency = Collections.singletonList(
mock(SharedLibraryInfo.class));
- when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg)))
- .thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
- 0, mHandler, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(missingDependency, pkg,
+ mComputer, 0, mHandler, callback);
callback.assertFailure();
assertThat(callback.error).hasMessageThat().contains(
@@ -128,12 +107,10 @@ public class InstallDependencyHelperTest {
// Return an empty list as missing dependency
PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
List<SharedLibraryInfo> missingDependency = Collections.emptyList();
- when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg)))
- .thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
- 0, mHandler, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(missingDependency, pkg,
+ mComputer, 0, mHandler, callback);
callback.assertSuccess();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 7444403f88c8..60e82501db1c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -58,8 +58,8 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(),
+ eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -149,11 +149,11 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(),
- any())
- verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
- any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(
+ any(), eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+ pkgListCaptor.capture(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(
+ any(), any(), any(), any())
var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -230,10 +230,10 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
- verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
- any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(
+ any(), eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(
+ any(), any(), any(), any())
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
diff --git a/services/tests/ondeviceintelligencetests/Android.bp b/services/tests/ondeviceintelligencetests/Android.bp
index a31a3fb65700..f235da2fea7f 100644
--- a/services/tests/ondeviceintelligencetests/Android.bp
+++ b/services/tests/ondeviceintelligencetests/Android.bp
@@ -44,14 +44,18 @@ android_test {
"truth",
"frameworks-base-testutils",
"androidx.test.rules",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_ondevice_intelligence_module"), {
+ "true": [
+ "service-ondeviceintelligence.impl",
+ ],
+ default: [],
+ }),
libs: [
"android.test.mock.stubs.system",
"android.test.base.stubs.system",
"android.test.runner.stubs.system",
],
-
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/services/tests/ondeviceintelligencetests/OWNERS b/services/tests/ondeviceintelligencetests/OWNERS
index 09774f78d712..a4fc7582a785 100644
--- a/services/tests/ondeviceintelligencetests/OWNERS
+++ b/services/tests/ondeviceintelligencetests/OWNERS
@@ -1 +1,3 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
+shiqing@google.com
+sandeepbandaru@google.com
+shivanker@google.com
diff --git a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
index d12579cd6b11..28ccb84c38f3 100644
--- a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
+++ b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.ondeviceintelligence.InferenceInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import android.util.Base64;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +37,8 @@ import java.util.List;
public class InferenceInfoStoreTest {
InferenceInfoStore inferenceInfoStore;
+ public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
@Before
public void setUp() {
inferenceInfoStore = new InferenceInfoStore(1000);
@@ -46,7 +47,7 @@ public class InferenceInfoStoreTest {
@Test
public void testInferenceInfoParsesFromBundleSuccessfully() throws Exception {
Bundle bundle = new Bundle();
- bundle.putByteArray(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putByteArray(INFERENCE_INFO_BUNDLE_KEY,
getInferenceInfoBytes(1, 1, 100));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
@@ -59,7 +60,7 @@ public class InferenceInfoStoreTest {
@Test
public void testInferenceInfoParsesFromPersistableBundleSuccessfully() throws Exception {
PersistableBundle bundle = new PersistableBundle();
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, 1, 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
@@ -74,11 +75,11 @@ public class InferenceInfoStoreTest {
public void testEvictionAfterMaxAge() throws Exception {
PersistableBundle bundle = new PersistableBundle();
long testStartTime = System.currentTimeMillis();
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 10,
testStartTime + 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 5,
testStartTime + 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 7248833d876c..4b2e850d08e7 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -64,6 +64,7 @@ import android.os.Binder;
import android.os.CpuHeadroomParamsInternal;
import android.os.GpuHeadroomParamsInternal;
import android.os.IBinder;
+import android.os.IHintManager;
import android.os.IHintSession;
import android.os.PerformanceHintManager;
import android.os.Process;
@@ -154,6 +155,8 @@ public class HintManagerServiceTest {
private ActivityManagerInternal mAmInternalMock;
@Mock
private PackageManager mMockPackageManager;
+ @Mock
+ private IHintManager.IHintManagerClient mClientCallback;
@Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -171,6 +174,24 @@ public class HintManagerServiceTest {
};
}
+ private SupportInfo makeDefaultSupportInfo() {
+ mSupportInfo = new SupportInfo();
+ mSupportInfo.usesSessions = true;
+ // By default, mark everything as fully supported
+ mSupportInfo.sessionHints = -1;
+ mSupportInfo.sessionModes = -1;
+ mSupportInfo.modes = -1;
+ mSupportInfo.boosts = -1;
+ mSupportInfo.sessionTags = -1;
+ mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+ mSupportInfo.headroom.isCpuSupported = true;
+ mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
+ mSupportInfo.headroom.isGpuSupported = true;
+ mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+ mSupportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo();
+ return mSupportInfo;
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -181,12 +202,7 @@ public class HintManagerServiceTest {
mConfig.eventFlagDescriptor = new MQDescriptor<Byte, Byte>();
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
- mSupportInfo = new SupportInfo();
- mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
- mSupportInfo.headroom.isCpuSupported = true;
- mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
- mSupportInfo.headroom.isGpuSupported = true;
- mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+ mSupportInfo = makeDefaultSupportInfo();
when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getNameForUid(anyInt())).thenReturn(TEST_APP_NAME);
when(mMockPackageManager.getApplicationInfo(eq(TEST_APP_NAME), anyInt()))
@@ -215,6 +231,7 @@ public class HintManagerServiceTest {
when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo);
when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
+ when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
}
@@ -409,8 +426,11 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
IBinder token = new Binder();
- final int threadCount =
- service.getBinderServiceInstance().getMaxGraphicsPipelineThreadsCount();
+ IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+ .registerClient(mClientCallback);
+
+ final int threadCount = data.maxGraphicsPipelineThreads;
+
long sessionPtr1 = 1111L;
long sessionId1 = 11111L;
CountDownLatch stopLatch1 = new CountDownLatch(1);
@@ -1255,6 +1275,53 @@ public class HintManagerServiceTest {
}
@Test
+ public void testCpuHeadroomInvalidParams() {
+ HintManagerService service = createService();
+ final CpuHeadroomParamsInternal param1 = new CpuHeadroomParamsInternal();
+ param1.calculationType = 100;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(param1);
+ });
+
+ final CpuHeadroomParamsInternal param2 = new CpuHeadroomParamsInternal();
+ param2.calculationWindowMillis = 49;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(param2);
+ });
+ param2.calculationWindowMillis = 10001;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(param2);
+ });
+
+ final CpuHeadroomParamsInternal param3 = new CpuHeadroomParamsInternal();
+ param3.tids = new int[]{1, 2, 3, 4, 5, 6};
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(param3);
+ });
+ }
+
+ @Test
+ public void testGpuHeadroomInvalidParams() {
+ HintManagerService service = createService();
+ final GpuHeadroomParamsInternal param1 = new GpuHeadroomParamsInternal();
+ param1.calculationType = 100;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(param1);
+ });
+
+ final GpuHeadroomParamsInternal param2 = new GpuHeadroomParamsInternal();
+ param2.calculationWindowMillis = 49;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(param2);
+ });
+ param2.calculationWindowMillis = 10001;
+ assertThrows(IllegalArgumentException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(param2);
+ });
+ }
+
+
+ @Test
public void testCpuHeadroomCache() throws Exception {
CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
CpuHeadroomParams halParams1 = new CpuHeadroomParams();
@@ -1400,4 +1467,67 @@ public class HintManagerServiceTest {
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
}
+
+ @Test
+ public void testRegisteringClient() throws Exception {
+ HintManagerService service = createService();
+ IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+ .registerClient(mClientCallback);
+ assertNotNull(data);
+ assertEquals(data.supportInfo, mSupportInfo);
+ }
+
+ @Test
+ public void testRegisteringClientOnV4() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(4);
+ HintManagerService service = createService();
+ IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+ .registerClient(mClientCallback);
+ assertNotNull(data);
+ assertEquals(data.supportInfo.usesSessions, true);
+ assertEquals(data.supportInfo.boosts, 0);
+ assertEquals(data.supportInfo.modes, 0);
+ assertEquals(data.supportInfo.sessionHints, 31);
+ assertEquals(data.supportInfo.sessionModes, 0);
+ assertEquals(data.supportInfo.sessionTags, 0);
+ assertEquals(data.powerHalVersion, 4);
+ assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE);
+ }
+
+ @Test
+ public void testRegisteringClientOnV5() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ HintManagerService service = createService();
+ IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+ .registerClient(mClientCallback);
+ assertNotNull(data);
+ assertEquals(data.supportInfo.usesSessions, true);
+ assertEquals(data.supportInfo.boosts, 0);
+ assertEquals(data.supportInfo.modes, 0);
+ assertEquals(data.supportInfo.sessionHints, 255);
+ assertEquals(data.supportInfo.sessionModes, 1);
+ assertEquals(data.supportInfo.sessionTags, 31);
+ assertEquals(data.powerHalVersion, 5);
+ assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE);
+ }
+
+ @Test
+ public void testSettingUpOldClientWhenUnsupported() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ // Mock unsupported to modify the default support behavior
+ when(mNativeWrapperMock.halGetHintSessionPreferredRate())
+ .thenReturn(-1L);
+ HintManagerService service = createService();
+ IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+ .registerClient(mClientCallback);
+ assertNotNull(data);
+ assertEquals(data.supportInfo.usesSessions, false);
+ assertEquals(data.supportInfo.boosts, 0);
+ assertEquals(data.supportInfo.modes, 0);
+ assertEquals(data.supportInfo.sessionHints, 0);
+ assertEquals(data.supportInfo.sessionModes, 0);
+ assertEquals(data.supportInfo.sessionTags, 0);
+ assertEquals(data.powerHalVersion, 5);
+ assertEquals(data.preferredRateNanos, -1);
+ }
}
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 709f83ba907d..73dcfe77e67f 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
@@ -36,6 +36,7 @@ import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ConditionVariable;
+import android.os.Handler;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -81,8 +82,9 @@ public class BatteryUsageStatsProviderTest {
.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
private MockClock mMockClock = mStatsRule.getMockClock();
- private MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
+ private MonotonicClock mMonotonicClock = mStatsRule.getMonotonicClock();
private Context mContext;
+ private PowerStatsStore mPowerStatsStore;
@Before
public void setup() throws IOException {
@@ -93,6 +95,9 @@ public class BatteryUsageStatsProviderTest {
} else {
mContext = InstrumentationRegistry.getContext();
}
+ mPowerStatsStore = spy(new PowerStatsStore(
+ new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()),
+ mStatsRule.getHandler()));
}
@Test
@@ -274,10 +279,7 @@ public class BatteryUsageStatsProviderTest {
powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
true);
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
- powerAttributor, mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
- mMonotonicClock);
+ BatteryUsageStatsProvider provider = createBatteryUsageStatsProvider(0);
return provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
}
@@ -331,30 +333,30 @@ public class BatteryUsageStatsProviderTest {
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
+ assertHistoryItem(batteryStats, item,
BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE,
null, 0, 3_600_000, 90, 1_000_000);
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
+ assertHistoryItem(batteryStats, item,
BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
null, 0, 3_600_000, 90, 1_000_000);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
+ assertHistoryItem(batteryStats, item,
BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
null, 0, 3_600_000, 90, 2_000_000);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
+ assertHistoryItem(batteryStats, item,
BatteryStats.HistoryItem.CMD_UPDATE,
BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START,
"foo", APP_UID, 3_600_000, 90, 3_000_000);
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
+ assertHistoryItem(batteryStats, item,
BatteryStats.HistoryItem.CMD_UPDATE,
BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH,
"foo", APP_UID, 3_600_000, 90, 3_001_000);
@@ -441,14 +443,15 @@ public class BatteryUsageStatsProviderTest {
assertThat(item.eventTag.string).startsWith(uid + " ");
assertThat(item.batteryChargeUah).isEqualTo(3_600_000);
assertThat(item.batteryLevel).isEqualTo(90);
- assertThat(item.time).isEqualTo((long) 1_000_000);
+ assertThat(item.time).isEqualTo(batteryStats.getMonotonicStartTime() + 1_000_000);
}
assertThat(expectedUid).isEqualTo(200);
}
- private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode,
- String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs) {
+ private void assertHistoryItem(MockBatteryStatsImpl batteryStats, BatteryStats.HistoryItem item,
+ int command, int eventCode, String tag, int uid, int batteryChargeUah, int batteryLevel,
+ long elapsedTimeMs) {
assertThat(item.cmd).isEqualTo(command);
assertThat(item.eventCode).isEqualTo(eventCode);
if (tag == null) {
@@ -460,7 +463,7 @@ public class BatteryUsageStatsProviderTest {
assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah);
assertThat(item.batteryLevel).isEqualTo(batteryLevel);
- assertThat(item.time).isEqualTo(elapsedTimeMs);
+ assertThat(item.time).isEqualTo(batteryStats.getMonotonicStartTime() + elapsedTimeMs);
}
@Test
@@ -566,38 +569,66 @@ public class BatteryUsageStatsProviderTest {
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
- assertThat(stats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isWithin(0.0001)
- .of(180.0); // 360 mA * 0.5 hours
- assertThat(stats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isEqualTo((10 + 20) * MINUTE_IN_MS);
- final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
- .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
- assertThat(uidBatteryConsumer
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isWithin(0.1)
- .of(180.0);
+ assertBatteryConsumer(stats, 180.0, (10 + 20) * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, APP_UID, 180.0, (10 + 20) * MINUTE_IN_MS);
stats.close();
}
@Test
public void accumulateBatteryUsageStats() throws Throwable {
- accumulateBatteryUsageStats(10000000, 1);
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ accumulateBatteryUsageStats(batteryStats, 10000000, 0);
// Accumulate every 200 bytes of battery history
- accumulateBatteryUsageStats(200, 2);
- accumulateBatteryUsageStats(50, 5);
+ accumulateBatteryUsageStats(batteryStats, 200, 2);
+ accumulateBatteryUsageStats(batteryStats, 50, 4);
// Accumulate on every invocation of accumulateBatteryUsageStats
- accumulateBatteryUsageStats(0, 7);
+ accumulateBatteryUsageStats(batteryStats, 0, 7);
}
- private void accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize,
- int expectedNumberOfUpdates) throws Throwable {
- BatteryStatsImpl batteryStats = spy(mStatsRule.getBatteryStats());
+ @Test
+ public void getAccumulatedBatteryUsageStats() throws Throwable {
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ // Only accumulate the first 25 minutes
+ accumulateBatteryUsageStats(batteryStats, 200, 1);
+
+ BatteryUsageStatsProvider batteryUsageStatsProvider = createBatteryUsageStatsProvider(200);
+
+ // At this point the last stored accumulated stats are `115 - 30 = 85` minutes old
+ BatteryUsageStats stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
+ new BatteryUsageStatsQuery.Builder()
+ .accumulated()
+ .setMaxStatsAgeMs(90 * MINUTE_IN_MS)
+ .build());
+
+ assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+ assertThat(stats.getStatsEndTimestamp()).isEqualTo(30 * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, 60.0, 10 * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, APP_UID, 60.0, 10 * MINUTE_IN_MS);
+
+ stats.close();
+
+ // Now force the usage stats to catch up to the current time
+ stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
+ new BatteryUsageStatsQuery.Builder()
+ .accumulated()
+ .setMaxStatsAgeMs(5 * MINUTE_IN_MS)
+ .build());
+
+ assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+ assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, 360.0, 60 * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, APP_UID, 360.0, 60 * MINUTE_IN_MS);
+
+ stats.close();
+ }
+
+ private void accumulateBatteryUsageStats(MockBatteryStatsImpl batteryStatsImpl,
+ int accumulatedBatteryUsageStatsSpanSize, int expectedNumberOfUpdates)
+ throws Throwable {
+ Handler handler = mStatsRule.getHandler();
+ MockBatteryStatsImpl batteryStats = spy(batteryStatsImpl);
// Note - these two are in microseconds
when(batteryStats.computeBatteryTimeRemaining(anyLong())).thenReturn(111_000L);
when(batteryStats.computeChargeTimeRemaining(anyLong())).thenReturn(777_000L);
@@ -610,82 +641,76 @@ public class BatteryUsageStatsProviderTest {
batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
}
- PowerStatsStore powerStatsStore = spy(new PowerStatsStore(
- new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()),
- mStatsRule.getHandler()));
- powerStatsStore.reset();
+ mPowerStatsStore.reset();
int[] count = new int[1];
doAnswer(inv -> {
count[0]++;
- return null;
- }).when(powerStatsStore).storePowerStatsSpan(any(PowerStatsSpan.class));
+ return inv.callRealMethod();
+ }).when(mPowerStatsStore).storePowerStatsSpan(any(PowerStatsSpan.class));
- MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext,
- powerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(),
- () -> 3500);
- for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT;
- powerComponentId++) {
- powerAttributor.setPowerComponentSupported(powerComponentId, true);
- }
- powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_ANY, true);
-
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
- powerAttributor, mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), powerStatsStore,
- accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
+ BatteryUsageStatsProvider batteryUsageStatsProvider = createBatteryUsageStatsProvider(
+ accumulatedBatteryUsageStatsSpanSize);
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
+ setTime(10 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
}
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
+ setTime(20 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOffLocked(APP_UID,
20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
}
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
+ setTime(30 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
}
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
+ // Make sure the accumulated stats are computed and saved before generating more history
+ mStatsRule.waitForBackgroundThread();
+
+ setTime(50 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOffLocked(APP_UID,
50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
}
setTime(55 * MINUTE_IN_MS);
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
// This section has not been saved yet, but should be added to the accumulated totals
+ setTime(80 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
}
- provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
+ setTime(110 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.noteFlashlightOffLocked(APP_UID,
110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
}
setTime(115 * MINUTE_IN_MS);
- // Pick up the remainder of battery history that has not yet been accumulated
- provider.accumulateBatteryUsageStats(batteryStats);
+ batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
mStatsRule.waitForBackgroundThread();
- BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats,
+ BatteryUsageStats stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().accumulated().build());
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
@@ -696,29 +721,55 @@ public class BatteryUsageStatsProviderTest {
assertThat(stats.getBatteryCapacity()).isEqualTo(4000); // from PowerProfile
// Total: 10 + 20 + 30 = 60
- assertThat(stats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ assertBatteryConsumer(stats, 360.0, 60 * MINUTE_IN_MS);
+ assertBatteryConsumer(stats, APP_UID, 360.0, 60 * MINUTE_IN_MS);
+ stats.close();
+
+ mStatsRule.waitForBackgroundThread();
+
+ assertThat(count[0]).isEqualTo(expectedNumberOfUpdates);
+ }
+
+ private BatteryUsageStatsProvider createBatteryUsageStatsProvider(
+ int accumulatedBatteryUsageStatsSpanSize) {
+ MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext,
+ mPowerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(),
+ () -> 3500);
+ for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ powerComponentId++) {
+ powerAttributor.setPowerComponentSupported(powerComponentId, true);
+ }
+ powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_ANY, true);
+
+ return new BatteryUsageStatsProvider(mContext, powerAttributor,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), mPowerStatsStore,
+ accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
+ }
+
+ private static void assertBatteryConsumer(BatteryUsageStats stats, double expectedPowerMah,
+ long expectedDurationMs) {
+ AggregateBatteryConsumer aggregatedConsumer = stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertThat(aggregatedConsumer
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
.isWithin(0.0001)
- .of(360.0); // 360 mA * 1.0 hour
- assertThat(stats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .of(expectedPowerMah);
+ assertThat(aggregatedConsumer
.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isEqualTo(60 * MINUTE_IN_MS);
+ .isEqualTo(expectedDurationMs);
+ }
+ private static void assertBatteryConsumer(BatteryUsageStats stats, int uid,
+ double expectedPowerMah, long expectedDurationMs) {
final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
- .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+ .filter(u -> u.getUid() == uid).findFirst().get();
assertThat(uidBatteryConsumer
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
.isWithin(0.1)
- .of(360.0);
+ .of(expectedPowerMah);
assertThat(uidBatteryConsumer
.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isEqualTo(60 * MINUTE_IN_MS);
-
- assertThat(count[0]).isEqualTo(expectedNumberOfUpdates);
-
- stats.close();
+ .isEqualTo(expectedDurationMs);
}
private void setTime(long timeMs) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index a3c7ece386c7..9e7e0b646047 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -41,6 +41,7 @@ import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.power.EnergyConsumerStats;
@@ -65,6 +66,7 @@ public class BatteryUsageStatsRule implements TestRule {
private final PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
private String mTestName;
private boolean mCreateTempDirectory;
private File mHistoryDir;
@@ -118,7 +120,7 @@ public class BatteryUsageStatsRule implements TestRule {
clearDirectory();
}
mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(),
- mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
+ mMockClock, mMonotonicClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
synchronized (mBatteryStats) {
@@ -144,6 +146,10 @@ public class BatteryUsageStatsRule implements TestRule {
return mMockClock;
}
+ public MonotonicClock getMonotonicClock() {
+ return mMonotonicClock;
+ }
+
public Handler getHandler() {
return mHandler;
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
index e392c5d190f7..3895cb480847 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
@@ -225,7 +225,10 @@ public class BluetoothPowerStatsCollectorTest {
}
private PowerStats collectPowerStats() {
- BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+ List<BluetoothActivityEnergyInfo> expected = new ArrayList<>();
+ List<BluetoothActivityEnergyInfo> observed = new ArrayList<>();
+ BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector,
+ (info, elapsedRealtimeMs, uptimeMs) -> observed.add(info));
collector.setEnabled(true);
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
@@ -236,6 +239,7 @@ public class BluetoothPowerStatsCollectorTest {
mockUidTraffic(APP_UID1, 100, 200),
mockUidTraffic(APP_UID2, 300, 400),
mockUidTraffic(ISOLATED_UID, 500, 600));
+ expected.add(mBluetoothActivityEnergyInfo);
mUidScanTimes.put(APP_UID1, 100);
@@ -248,6 +252,7 @@ public class BluetoothPowerStatsCollectorTest {
mockUidTraffic(APP_UID1, 1100, 2200),
mockUidTraffic(APP_UID2, 3300, 4400),
mockUidTraffic(ISOLATED_UID, 5500, 6600));
+ expected.add(mBluetoothActivityEnergyInfo);
mUidScanTimes.clear();
mUidScanTimes.put(APP_UID1, 200);
@@ -257,7 +262,10 @@ public class BluetoothPowerStatsCollectorTest {
mockConsumedEnergy(777, 64321);
mStatsRule.setTime(20000, 20000);
- return collector.collectStats();
+ PowerStats powerStats = collector.collectStats();
+
+ assertThat(observed).isEqualTo(expected);
+ return powerStats;
}
private void mockConsumedEnergy(int consumerId, long energyUWs) {
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 b374a3202fa2..9a38209a7d17 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
@@ -77,9 +77,15 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
new PowerStatsUidResolver());
}
- MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
- Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
- super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
+ MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock,
+ File historyDirectory, Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+ this(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
+ powerStatsUidResolver);
+ }
+
+ MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, MonotonicClock monotonicClock,
+ File historyDirectory, Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+ super(config, clock, monotonicClock, historyDirectory, handler,
mock(PlatformIdleStateCallback.class), mock(EnergyStatsRetriever.class),
mock(UserInfoProvider.class), mockPowerProfile(),
new CpuScalingPolicies(new SparseArray<>(), new SparseArray<>()),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
index 2c580e5ab0d2..60131861ce89 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -170,7 +170,7 @@ public class BluetoothPowerStatsProcessorTest {
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
() -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
- BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+ BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
collector.setEnabled(true);
mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
mockUidTraffic(APP_UID1, 100, 200),
@@ -271,7 +271,7 @@ public class BluetoothPowerStatsProcessorTest {
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
() -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
- BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+ BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
collector.setEnabled(true);
mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
mockUidTraffic(APP_UID1, 100, 200),
@@ -371,7 +371,7 @@ public class BluetoothPowerStatsProcessorTest {
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
() -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
- BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+ BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
collector.setEnabled(true);
mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
mockUidTraffic(APP_UID1, 100, 200),
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 f312bedca82c..3bdbcb50e601 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
@@ -263,6 +263,34 @@ public class PowerStatsAggregatorTest {
});
}
+ @Test
+ public void emptyHistory() {
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+ "majorDrain", 1, null, 0, 1, new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+
+ mHistory.forceRecordAllHistory();
+
+ advance(1000);
+
+ long firstItemTimestamp = mMonotonicClock.monotonicTime();
+ powerStats.stats = new long[]{24};
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ advance(1000);
+
+ long secondItemTimestamp = mMonotonicClock.monotonicTime();
+ powerStats.stats = new long[]{42};
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ mAggregator.aggregatePowerStats(mHistory, firstItemTimestamp + 1, secondItemTimestamp,
+ stats -> {
+ mAggregatedStatsCount++;
+ });
+
+ assertThat(mAggregatedStatsCount).isEqualTo(0);
+ }
+
private void advance(long durationMs) {
mClock.realtime += durationMs;
mClock.uptime += durationMs;
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 38fe6134d992..d243f92a139f 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
@@ -408,7 +408,7 @@ public class PowerStatsExporterTest {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
new String[]{"cu570m"},
/* includeProcessStateData */ true, true, true, /* powerThreshold */ 0);
- exportAggregatedPowerStats(builder, 3700, 6700);
+ exportAggregatedPowerStats(builder, 3700, 7500);
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
diff --git a/services/tests/security/intrusiondetection/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml
index b30710d9bcbe..d58e0d84ee32 100644
--- a/services/tests/security/intrusiondetection/AndroidManifest.xml
+++ b/services/tests/security/intrusiondetection/AndroidManifest.xml
@@ -19,18 +19,10 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" />
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
<application android:testOnly="true" android:debuggable="true" android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner"/>
- <receiver android:name="com.android.server.security.intrusiondetection.IntrusionDetectionAdminReceiver"
- android:permission="android.permission.BIND_DEVICE_ADMIN"
- android:exported="true">
- <meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin"/>
- <intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
- </intent-filter>
- </receiver>
</application>
<queries>
diff --git a/services/tests/security/intrusiondetection/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml
index 6489dea4a508..0d211585958a 100644
--- a/services/tests/security/intrusiondetection/AndroidTest.xml
+++ b/services/tests/security/intrusiondetection/AndroidTest.xml
@@ -24,6 +24,10 @@
<option name="install-arg" value="-t" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop intrusiondetection_service_name com.android.coretests.apps.testapp/.TestLoggingService" />
+ </target_preparer>
+
<option name="test-tag" value="IntrusionDetectionServiceTests" />
<test class="com.android.tradefed.testtype.InstrumentationTest" >
<option name="package" value="com.android.server.security.intrusiondetection.tests" />
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index e505ebeb2946..5cba6b2c3e67 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -16,12 +16,12 @@
package com.android.server.security.intrusiondetection;
+import static android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE;
import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE;
import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
@@ -45,7 +45,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
-import android.os.Bundle;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.RemoteException;
@@ -61,17 +60,13 @@ import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import com.android.bedstead.harrier.BedsteadJUnit4;
-import com.android.bedstead.harrier.annotations.AfterClass;
-import com.android.bedstead.harrier.annotations.BeforeClass;
import com.android.bedstead.multiuser.annotations.RequireRunOnSystemUser;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.devicepolicy.DeviceOwner;
-import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.permissions.CommonPermissions;
import com.android.bedstead.permissions.PermissionContext;
import com.android.bedstead.permissions.annotations.EnsureHasPermission;
import com.android.coretests.apps.testapp.LocalIntrusionDetectionEventTransport;
-import com.android.internal.infra.AndroidFuture;
import com.android.server.ServiceThread;
import org.junit.Before;
@@ -129,28 +124,6 @@ public class IntrusionDetectionServiceTest {
"com.android.coretests.apps.testapp";
private static final String TEST_SERVICE = TEST_PKG + ".TestLoggingService";
- @BeforeClass
- public static void setDeviceOwner() {
- ComponentName admin =
- new ComponentName(
- ApplicationProvider.getApplicationContext(),
- IntrusionDetectionAdminReceiver.class);
- try {
- sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin);
- } catch (NeneException e) {
- fail("Failed to set device owner " + admin.flattenToString() + ": " + e);
- }
- }
-
- @AfterClass
- public static void removeDeviceOwner() {
- try {
- sDeviceOwner.remove();
- } catch (NeneException e) {
- fail("Failed to remove device owner : " + e);
- }
- }
-
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
@@ -159,6 +132,7 @@ public class IntrusionDetectionServiceTest {
mPermissionEnforcer = new FakePermissionEnforcer();
mPermissionEnforcer.grant(READ_INTRUSION_DETECTION_STATE);
mPermissionEnforcer.grant(MANAGE_INTRUSION_DETECTION_STATE);
+ mPermissionEnforcer.grant(BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE);
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
@@ -178,6 +152,7 @@ public class IntrusionDetectionServiceTest {
}
@Test
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testRemoveStateCallback_NoPermission() {
mPermissionEnforcer.revoke(READ_INTRUSION_DETECTION_STATE);
StateCallback scb = new StateCallback();
@@ -229,6 +204,7 @@ public class IntrusionDetectionServiceTest {
}
@Test
+ @Ignore("Unit test does not run as system service UID")
public void testRemoveStateCallback() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
@@ -239,7 +215,6 @@ public class IntrusionDetectionServiceTest {
assertEquals(STATE_DISABLED, scb1.mState);
assertEquals(STATE_DISABLED, scb2.mState);
- doReturn(true).when(mDataAggregator).initialize();
doReturn(true).when(mIntrusionDetectionEventTransportConnection).initialize();
mIntrusionDetectionService.getBinderService().removeStateCallback(scb2);
@@ -252,6 +227,7 @@ public class IntrusionDetectionServiceTest {
assertNull(ccb.mErrorCode);
}
+ @Ignore("Unit test does not run as system service UID")
@Test
public void testEnable_FromDisabled_TwoStateCallbacks() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
@@ -412,39 +388,13 @@ public class IntrusionDetectionServiceTest {
}
@Test
- @RequireRunOnSystemUser
- public void testDataSources_Initialize_HasDeviceOwner() throws Exception {
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator);
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator);
-
- assertTrue(networkLogSource.initialize());
- assertTrue(securityLogSource.initialize());
- }
-
- @Test
- @RequireRunOnSystemUser
- public void testDataSources_Initialize_NoDeviceOwner() throws Exception {
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator);
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator);
- ComponentName admin = sDeviceOwner.componentName();
-
- try {
- sDeviceOwner.remove();
- assertFalse(networkLogSource.initialize());
- assertFalse(securityLogSource.initialize());
- } finally {
- sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin);
- }
- }
-
- @Test
+ @Ignore("Unit test does not run as system service UID")
@RequireRunOnSystemUser
@EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testDataAggregator_AddSecurityEvent() throws Exception {
mIntrusionDetectionService.setState(STATE_ENABLED);
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
- assertTrue(mDataAggregator.initialize());
// SecurityLogging generates a number of events and callbacks, so create a latch to wait for
// the given event.
@@ -488,12 +438,12 @@ public class IntrusionDetectionServiceTest {
@Test
@RequireRunOnSystemUser
+ @Ignore("Unit test does not run as system service UID")
@EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testDataAggregator_AddNetworkEvent() throws Exception {
mIntrusionDetectionService.setState(STATE_ENABLED);
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
- assertTrue(mDataAggregator.initialize());
// Network logging may log multiple and callbacks, so create a latch to wait for
// the given event.
@@ -578,6 +528,9 @@ public class IntrusionDetectionServiceTest {
}
@Test
+ @RequireRunOnSystemUser
+ @EnsureHasPermission(
+ android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE)
public void test_StartIntrusionDetectionEventTransportService() {
final String TAG = "test_StartIntrusionDetectionEventTransportService";
ServiceConnection serviceConnection = null;
@@ -639,6 +592,20 @@ public class IntrusionDetectionServiceTest {
return serviceConnection;
}
+ @Test
+ @RequireRunOnSystemUser
+ @EnsureHasPermission(
+ android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE)
+ public void testIntrusionDetectionEventTransportConnection_isValidAndBinds()
+ throws InterruptedException {
+ IntrusionDetectionEventTransportConnection intrusionDetectionEventTransportConnection =
+ new IntrusionDetectionEventTransportConnection(mContext);
+ // In a real scenario, the connection will be initialized by the service.
+ // Just to show that the connection is valid and able to bind,
+ // we initialize it here.
+ assertTrue(intrusionDetectionEventTransportConnection.initialize());
+ }
+
private class MockInjector implements IntrusionDetectionService.Injector {
private final Context mContext;
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
index 7cc75ab70571..a1a7e29094d2 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
@@ -16,9 +16,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.coretests.apps.testapp">
+ <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
<application>
<service android:name=".TestLoggingService"
- android:exported="true" />
+ android:exported="true"
+ android:permission="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
</application>
</manifest> \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 8024915692aa..71a2651a0f14 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -16,7 +16,12 @@
package com.android.server;
-import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
+import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -24,19 +29,27 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.telecom.TelecomManager;
import android.test.mock.MockContentResolver;
import android.testing.TestableLooper;
@@ -55,6 +68,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -62,6 +76,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link GestureLauncherService}.
@@ -90,9 +106,32 @@ public class GestureLauncherServiceTest {
private @Mock TelecomManager mTelecomManager;
private @Mock MetricsLogger mMetricsLogger;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private QuickAccessWalletClient mQuickAccessWalletClient;
private MockContentResolver mContentResolver;
private GestureLauncherService mGestureLauncherService;
+ private Context mInstrumentationContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+
+ private static final String LAUNCH_TEST_WALLET_ACTION = "LAUNCH_TEST_WALLET_ACTION";
+ private static final String LAUNCH_FALLBACK_ACTION = "LAUNCH_FALLBACK_ACTION";
+ private PendingIntent mGesturePendingIntent =
+ PendingIntent.getBroadcast(
+ mInstrumentationContext,
+ 0,
+ new Intent(LAUNCH_TEST_WALLET_ACTION),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+ private PendingIntent mFallbackPendingIntent =
+ PendingIntent.getBroadcast(
+ mInstrumentationContext,
+ 0,
+ new Intent(LAUNCH_FALLBACK_ACTION),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@BeforeClass
public static void oneTimeInitialization() {
if (Looper.myLooper() == null) {
@@ -115,9 +154,49 @@ public class GestureLauncherServiceTest {
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent());
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
+
+ mGestureLauncherService =
+ new GestureLauncherService(
+ mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger);
+
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ }
+
+ private WalletLaunchedReceiver registerWalletLaunchedReceiver(String action) {
+ IntentFilter filter = new IntentFilter(action);
+ WalletLaunchedReceiver receiver = new WalletLaunchedReceiver();
+ mInstrumentationContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
+ return receiver;
+ }
+
+ /**
+ * A simple {@link BroadcastReceiver} implementation that counts down a {@link CountDownLatch}
+ * when a matching message is received
+ */
+ private static final class WalletLaunchedReceiver extends BroadcastReceiver {
+ private static final int TIMEOUT_SECONDS = 3;
- mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger,
- mUiEventLogger);
+ private final CountDownLatch mLatch;
+
+ WalletLaunchedReceiver() {
+ mLatch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLatch.countDown();
+ context.unregisterReceiver(this);
+ }
+
+ Boolean waitUntilShown() {
+ try {
+ return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
}
@Test
@@ -134,37 +213,123 @@ public class GestureLauncherServiceTest {
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(0);
- assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
- mContext, FAKE_USER_ID));
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ }
assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertTrue(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertTrue(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
public void testIsEmergencyGestureSettingEnabled_settingDisabled() {
withEmergencyGestureEnabledConfigValue(true);
withEmergencyGestureEnabledSettingValue(false);
@@ -245,12 +410,9 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_firstPowerDownCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
- long eventTime = INITIAL_EVENT_TIME_MILLIS +
- CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ long eventTime = INITIAL_EVENT_TIME_MILLIS + POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
boolean interactive = true;
@@ -284,8 +446,12 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -298,7 +464,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -309,7 +475,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -329,8 +495,12 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -343,7 +513,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -354,7 +524,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -401,7 +571,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -422,10 +592,7 @@ public class GestureLauncherServiceTest {
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupComplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -437,7 +604,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -450,7 +617,7 @@ public class GestureLauncherServiceTest {
verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
verify(mMetricsLogger)
- .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+ .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
verify(mUiEventLogger, times(1))
.log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
@@ -470,15 +637,145 @@ public class GestureLauncherServiceTest {
}
@Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void
+ testInterceptPowerKeyDown_fiveInboundPresses_walletAndEmergencyEnabled_bothLaunch() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableEmergencyGesture();
+ enableWalletGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ assertTrue(receiver.waitUntilShown());
+
+ // Presses 3 and 4 should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+ }
+
+ // Fifth button press should trigger the emergency flow
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ verify(mUiEventLogger, times(1))
+ .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_intervalInBoundsWalletPowerGesture() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+ assertTrue(receiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletGestureOn_quickAccessWalletServiceUnavailable() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+
+ assertFalse(receiver.waitUntilShown());
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_walletGestureOn_userSetupIncomplete() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+ withUserSetupCompleteValue(false);
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ assertFalse(receiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletPowerGesture_nullPendingIntent() {
+ WalletLaunchedReceiver gestureReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(null);
+ WalletLaunchedReceiver fallbackReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+ setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ assertFalse(gestureReceiver.waitUntilShown());
+ assertTrue(fallbackReceiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletPowerGesture_intervalOutOfBounds() {
+ WalletLaunchedReceiver gestureReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(null);
+ WalletLaunchedReceiver fallbackReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+ setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ assertFalse(gestureReceiver.waitUntilShown());
+ assertFalse(fallbackReceiver.waitUntilShown());
+ }
+
+ @Test
public void
testInterceptPowerKeyDown_fiveInboundPresses_cameraAndEmergencyEnabled_bothLaunch() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- withEmergencyGestureEnabledConfigValue(true);
- withEmergencyGestureEnabledSettingValue(true);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- mGestureLauncherService.updateEmergencyGestureEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
+ enableEmergencyGesture();
// First button press does nothing
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -491,7 +788,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 2nd button triggers camera
eventTime += interval;
@@ -507,7 +804,7 @@ public class GestureLauncherServiceTest {
verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
verify(mMetricsLogger)
- .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+ .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
verify(mUiEventLogger, times(1))
.log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
@@ -580,7 +877,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 3 more button presses which should not trigger any gesture (camera gesture disabled)
for (int i = 0; i < 3; i++) {
eventTime += interval;
@@ -634,7 +931,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 3 more button presses which should not trigger any gesture, but intercepts action.
for (int i = 0; i < 3; i++) {
eventTime += interval;
@@ -737,7 +1034,7 @@ public class GestureLauncherServiceTest {
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
- interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@@ -765,7 +1062,7 @@ public class GestureLauncherServiceTest {
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
- interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@@ -916,7 +1213,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
@@ -947,9 +1244,7 @@ public class GestureLauncherServiceTest {
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
withUserSetupCompleteValue(false);
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -962,7 +1257,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -973,7 +1268,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -995,9 +1290,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1009,7 +1302,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1020,7 +1313,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1042,9 +1335,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1067,7 +1358,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1087,8 +1378,12 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1101,7 +1396,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1112,7 +1407,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1146,7 +1441,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1156,7 +1451,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1202,7 +1497,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1223,10 +1518,7 @@ public class GestureLauncherServiceTest {
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupComplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1238,7 +1530,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1250,7 +1542,7 @@ public class GestureLauncherServiceTest {
verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
verify(mMetricsLogger)
- .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+ .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
verify(mUiEventLogger, times(1))
.log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
@@ -1272,9 +1564,7 @@ public class GestureLauncherServiceTest {
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupIncomplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
withUserSetupCompleteValue(false);
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1287,7 +1577,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1298,7 +1588,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1332,7 +1622,7 @@ public class GestureLauncherServiceTest {
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1343,7 +1633,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1365,9 +1655,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1390,7 +1678,7 @@ public class GestureLauncherServiceTest {
assertFalse(outLaunched.value);
verify(mMetricsLogger, never())
- .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
verify(mUiEventLogger, never()).log(any());
final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1414,7 +1702,7 @@ public class GestureLauncherServiceTest {
* @return last event time.
*/
private long triggerEmergencyGesture() {
- return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
+ return triggerEmergencyGesture(POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
}
/**
@@ -1473,6 +1761,27 @@ public class GestureLauncherServiceTest {
.thenReturn(enableConfigValue);
}
+ private void withDoubleTapPowerEnabledConfigValue(boolean enable) {
+ when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled))
+ .thenReturn(enable);
+ }
+
+ private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ enable ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
+ private void withDefaultDoubleTapPowerGestureAction(int action) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ action,
+ UserHandle.USER_CURRENT);
+ }
+
private void withEmergencyGestureEnabledConfigValue(boolean enableConfigValue) {
when(mResources.getBoolean(
com.android.internal.R.bool.config_emergencyGestureEnabled))
@@ -1510,4 +1819,72 @@ public class GestureLauncherServiceTest {
userSetupCompleteValue,
UserHandle.USER_CURRENT);
}
+
+ private void setUpGetGestureTargetActivityPendingIntent(PendingIntent pendingIntent) {
+ doAnswer(
+ invocation -> {
+ QuickAccessWalletClient.GesturePendingIntentCallback callback =
+ (QuickAccessWalletClient.GesturePendingIntentCallback)
+ invocation.getArguments()[1];
+ callback.onGesturePendingIntentRetrieved(pendingIntent);
+ return null;
+ })
+ .when(mQuickAccessWalletClient)
+ .getGestureTargetActivityPendingIntent(any(), any());
+ }
+
+ private void setUpWalletFallbackPendingIntent(PendingIntent pendingIntent) {
+ doAnswer(
+ invocation -> {
+ QuickAccessWalletClient.WalletPendingIntentCallback callback =
+ (QuickAccessWalletClient.WalletPendingIntentCallback)
+ invocation.getArguments()[1];
+ callback.onWalletPendingIntentRetrieved(pendingIntent);
+ return null;
+ })
+ .when(mQuickAccessWalletClient)
+ .getWalletPendingIntent(any(), any());
+ }
+
+ private void enableWalletGesture() {
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerEnabledConfigValue(true);
+
+ mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void enableEmergencyGesture() {
+ withEmergencyGestureEnabledConfigValue(true);
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void enableCameraGesture() {
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ }
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
+ long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
+ KeyEvent keyEvent =
+ new KeyEvent(
+ IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted =
+ mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+ assertEquals(intercepted, expectedIntercept);
+ assertEquals(outLaunched.value, expectedOutLaunchedValue);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
index fde3422b1ff3..17f5ebb3b02a 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -130,9 +130,9 @@ public class GameManagerServiceSettingsTests {
assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4));
// test game mode configs
- assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
- assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
- GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+ assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_1));
+ assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_3));
+ GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
assertNotNull(config);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
@@ -152,7 +152,7 @@ public class GameManagerServiceSettingsTests {
assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
assertFalse(batteryConfig.getUseAngle());
- config = settings.getConfigOverride(PACKAGE_NAME_4);
+ config = settings.getConfigOverrideLocked(PACKAGE_NAME_4);
assertNotNull(config);
GameModeConfiguration customConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_CUSTOM);
@@ -177,7 +177,7 @@ public class GameManagerServiceSettingsTests {
GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertTrue(settings.readPersistentDataLocked());
- final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_1);
+ final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_1);
assertNotNull(config);
final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
@@ -218,7 +218,7 @@ public class GameManagerServiceSettingsTests {
assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
- final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+ final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
assertNotNull(config);
final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
@@ -248,7 +248,7 @@ public class GameManagerServiceSettingsTests {
GameModeConfiguration batteryConfig = config.getOrAddDefaultGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
batteryConfig.setScaling(0.77f);
- settings.setConfigOverride(PACKAGE_NAME_2, config);
+ settings.setConfigOverrideLocked(PACKAGE_NAME_2, config);
// set config for app4
config = new GamePackageConfiguration(PACKAGE_NAME_4);
@@ -256,15 +256,15 @@ public class GameManagerServiceSettingsTests {
GameManager.GAME_MODE_CUSTOM);
customConfig.setScaling(0.4f);
customConfig.setFpsStr("30");
- settings.setConfigOverride(PACKAGE_NAME_4, config);
+ settings.setConfigOverrideLocked(PACKAGE_NAME_4, config);
settings.writePersistentDataLocked();
// clear the settings in memory
- settings.removeGame(PACKAGE_NAME_1);
- settings.removeGame(PACKAGE_NAME_2);
- settings.removeGame(PACKAGE_NAME_3);
- settings.removeGame(PACKAGE_NAME_4);
+ settings.removeGameLocked(PACKAGE_NAME_1);
+ settings.removeGameLocked(PACKAGE_NAME_2);
+ settings.removeGameLocked(PACKAGE_NAME_3);
+ settings.removeGameLocked(PACKAGE_NAME_4);
// read back in and verify
assertTrue(settings.readPersistentDataLocked());
@@ -273,9 +273,9 @@ public class GameManagerServiceSettingsTests {
assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4));
- config = settings.getConfigOverride(PACKAGE_NAME_1);
+ config = settings.getConfigOverrideLocked(PACKAGE_NAME_1);
assertNull(config);
- config = settings.getConfigOverride(PACKAGE_NAME_2);
+ config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
assertNotNull(config);
batteryConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY);
assertNotNull(batteryConfig);
@@ -292,7 +292,7 @@ public class GameManagerServiceSettingsTests {
assertEquals(performanceConfig.getFpsStr(), "60");
assertTrue(performanceConfig.getUseAngle());
- config = settings.getConfigOverride(PACKAGE_NAME_4);
+ config = settings.getConfigOverrideLocked(PACKAGE_NAME_4);
assertNotNull(config);
customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
assertNotNull(customConfig);
diff --git a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
index 75258f0aa7e0..d2db999d72ca 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.server.autofill;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
import static com.google.common.truth.Truth.assertThat;
@@ -129,4 +131,57 @@ public class PresentationEventLoggerTest {
assertThat(event).isNotNull();
assertThat(event.mDisplayPresentationType).isEqualTo(3);
}
+
+ @Test
+ public void testNoSuggestionsTextFiltered() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+ AutofillId id = new AutofillId(13);
+ AutofillValue initialValue = AutofillValue.forText("hello");
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetFocusedId(id);
+ pEventLogger.maybeSetNoPresentationEventReasonSuggestionsFiltered(initialValue);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ int NO_SUGGESTIONS =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
+ assertThat(event.mNoPresentationReason).isEqualTo(NO_SUGGESTIONS);
+ }
+
+ @Test
+ public void testSuggestionsTextNotFiltered() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+ AutofillId id = new AutofillId(13);
+ AutofillValue initialValue = null;
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetFocusedId(id);
+ pEventLogger.maybeSetNoPresentationEventReasonSuggestionsFiltered(initialValue);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mNoPresentationReason).isNotEqualTo(
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT);
+ }
+
+ @Test
+ public void testNotShownReasonNotOverridden() {
+
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetNoPresentationEventReason(AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED);
+ // Not allowed - no op
+ pEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mNoPresentationReason).isEqualTo(AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 5127b2d11e44..c6df146dd42a 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -22,6 +22,8 @@ import static com.android.compatibility.common.util.PollingCheck.waitFor;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@@ -921,6 +923,41 @@ public final class DeviceStateManagerServiceTest {
});
}
+ @Test
+ public void shouldShowRdmEduDialog1() {
+ // RDM V1 Cases
+ assertTrue(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ false /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed (no-op) */));
+
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ false /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed (no-op) */));
+
+ // RDM V2 Cases
+ // hasControlDeviceStatePermission = false
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed */));
+ assertTrue(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed */));
+
+ // hasControlDeviceStatePermission = true
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed */));
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed */));
+ }
+
/**
* Common code to verify the handling of FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP flag.
*
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 27486b71aa18..71a05f3b8509 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -286,6 +286,25 @@ public final class ShareTargetPredictorTest {
}
@Test
+ public void testPredictTargets_emptyIntentFilter() {
+ Bundle bundle = new Bundle();
+ IntentFilter filter = new IntentFilter();
+ bundle.putObject(ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY, filter);
+ AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
+ .setUiSurface(UI_SURFACE_SHARE)
+ .setPredictedTargetCount(NUM_PREDICTED_TARGETS)
+ .setExtras(bundle)
+ .build();
+ mPredictor = new ShareTargetPredictor(
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext);
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ assertThat(mAppTargetCaptor.getValue()).isEmpty();
+ }
+
+ @Test
public void testPredictTargets_noSharingHistoryRankedByShortcutRank() {
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 3));
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 2));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index a0f2395f5203..d70ffd2ec050 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -159,7 +159,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for the first launch path, no settings file available.
*/
- public void testFirstInitialize() {
+ public void FirstInitialize() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
}
@@ -167,7 +167,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* Test for {@link ShortcutService#getLastResetTimeLocked()} and
* {@link ShortcutService#getNextResetTimeLocked()}.
*/
- public void testUpdateAndGetNextResetTimeLocked() {
+ public void UpdateAndGetNextResetTimeLocked() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
@@ -196,7 +196,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for the restoration from saved file.
*/
- public void testInitializeFromSavedFile() {
+ public void InitializeFromSavedFile() {
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -220,7 +220,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Add various broken cases.
}
- public void testLoadConfig() {
+ public void LoadConfig() {
mService.updateConfigurationLocked(
ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
@@ -261,22 +261,22 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
- public void testGetMaxDynamicShortcutCount() {
+ public void GetMaxDynamicShortcutCount() {
assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
}
/** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
- public void testGetRemainingCallCount() {
+ public void GetRemainingCallCount() {
assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
}
- public void testGetIconMaxDimensions() {
+ public void GetIconMaxDimensions() {
assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
}
/** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
- public void testGetRateLimitResetTime() {
+ public void GetRateLimitResetTime() {
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
@@ -284,7 +284,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
}
- public void testSetDynamicShortcuts() {
+ public void SetDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_0);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -354,7 +354,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testAddDynamicShortcuts() {
+ public void AddDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_0);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -402,7 +402,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPushDynamicShortcut() {
+ public void PushDynamicShortcut() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+ ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
@@ -543,7 +543,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
}
- public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
+ public void PushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
throws InterruptedException {
mService.updateConfigurationLocked(
ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");
@@ -594,7 +594,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_2), any(), eq(USER_0));
}
- public void testUnlimitedCalls() {
+ public void UnlimitedCalls() {
setCaller(CALLING_PACKAGE_1, USER_0);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -625,7 +625,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(3, mManager.getRemainingCallCount());
}
- public void testPublishWithNoActivity() {
+ public void PublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_10, true);
@@ -731,7 +731,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPublishWithNoActivity_noMainActivityInPackage() {
+ public void PublishWithNoActivity_noMainActivityInPackage() {
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
@@ -750,7 +750,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testDeleteDynamicShortcuts() {
+ public void DeleteDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -791,7 +791,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(2, mManager.getRemainingCallCount());
}
- public void testDeleteAllDynamicShortcuts() {
+ public void DeleteAllDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -820,7 +820,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(1, mManager.getRemainingCallCount());
}
- public void testIcons() throws IOException {
+ public void Icons() throws IOException {
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1034,7 +1034,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
*/
}
- public void testCleanupDanglingBitmaps() throws Exception {
+ public void CleanupDanglingBitmaps() throws Exception {
assertBitmapDirectories(USER_0, EMPTY_STRINGS);
assertBitmapDirectories(USER_10, EMPTY_STRINGS);
@@ -1203,7 +1203,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
maxSize));
}
- public void testShrinkBitmap() {
+ public void ShrinkBitmap() {
checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1226,7 +1226,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
return out.getFile();
}
- public void testOpenIconFileForWrite() throws IOException {
+ public void OpenIconFileForWrite() throws IOException {
mInjectedCurrentTimeMillis = 1000;
final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
@@ -1300,7 +1300,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(p11_1_3.getName().contains("_"));
}
- public void testUpdateShortcuts() {
+ public void UpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -1431,7 +1431,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_icons() {
+ public void UpdateShortcuts_icons() {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
@@ -1525,7 +1525,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutManagerGetShortcuts_shortcutTypes() {
+ public void ShortcutManagerGetShortcuts_shortcutTypes() {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
@@ -1616,7 +1616,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s1", "s2");
}
- public void testCachedShortcuts() {
+ public void CachedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1700,7 +1700,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s2");
}
- public void testCachedShortcuts_accessShortcutsPermission() {
+ public void CachedShortcuts_accessShortcutsPermission() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1742,7 +1742,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
}
- public void testCachedShortcuts_canPassShortcutLimit() {
+ public void CachedShortcuts_canPassShortcutLimit() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -1780,7 +1780,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for launcher side APIs ===
- public void testGetShortcuts() {
+ public void GetShortcuts() {
// Set up shortcuts.
@@ -1997,7 +1997,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s1", "s3");
}
- public void testGetShortcuts_shortcutKinds() throws Exception {
+ public void GetShortcuts_shortcutKinds() throws Exception {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -2108,7 +2108,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testGetShortcuts_resolveStrings() throws Exception {
+ public void GetShortcuts_resolveStrings() throws Exception {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
.setId("id")
@@ -2156,7 +2156,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testGetShortcuts_personsFlag() {
+ public void GetShortcuts_personsFlag() {
ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
.setShortLabel("label")
.setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
@@ -2204,7 +2204,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
// TODO resource
- public void testGetShortcutInfo() {
+ public void GetShortcutInfo() {
// Create shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
@@ -2279,7 +2279,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals("ABC", findById(list, "s1").getTitle());
}
- public void testPinShortcutAndGetPinnedShortcuts() {
+ public void PinShortcutAndGetPinnedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2360,7 +2360,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* This is similar to the above test, except it used "disable" instead of "remove". It also
* does "enable".
*/
- public void testDisableAndEnableShortcuts() {
+ public void DisableAndEnableShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2485,7 +2485,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testDisableShortcuts_thenRepublish() {
+ public void DisableShortcuts_thenRepublish() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -2555,7 +2555,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPinShortcutAndGetPinnedShortcuts_multi() {
+ public void PinShortcutAndGetPinnedShortcuts_multi() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -2831,7 +2831,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPinShortcutAndGetPinnedShortcuts_assistant() {
+ public void PinShortcutAndGetPinnedShortcuts_assistant() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -2887,7 +2887,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+ public void PinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -3476,7 +3476,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testStartShortcut() {
+ public void StartShortcut() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
final ShortcutInfo s1_1 = makeShortcut(
@@ -3611,7 +3611,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check extra, etc
}
- public void testLauncherCallback() throws Throwable {
+ public void LauncherCallback() throws Throwable {
// Disable throttling for this test.
mService.updateConfigurationLocked(
ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
@@ -3777,7 +3777,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.isEmpty();
}
- public void testLauncherCallback_crossProfile() throws Throwable {
+ public void LauncherCallback_crossProfile() throws Throwable {
prepareCrossProfileDataSet();
final Handler h = new Handler(Looper.getMainLooper());
@@ -3900,7 +3900,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for persisting ===
- public void testSaveAndLoadUser_empty() {
+ public void SaveAndLoadUser_empty() {
assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
@@ -3917,7 +3917,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Try save and load, also stop/start the user.
*/
- public void testSaveAndLoadUser() {
+ public void SaveAndLoadUser() {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4058,7 +4058,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
- public void testLoadCorruptedShortcuts() throws Exception {
+ public void LoadCorruptedShortcuts() throws Exception {
initService();
addPackage("com.android.chrome", 0, 0);
@@ -4072,7 +4072,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
}
- public void testSaveCorruptAndLoadUser() throws Exception {
+ public void SaveCorruptAndLoadUser() throws Exception {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4228,7 +4228,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
- public void testCleanupPackage() {
+ public void CleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
@@ -4505,7 +4505,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.saveDirtyInfo();
}
- public void testCleanupPackage_republishManifests() {
+ public void CleanupPackage_republishManifests() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
@@ -4573,7 +4573,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHandleGonePackage_crossProfile() {
+ public void HandleGonePackage_crossProfile() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -4845,7 +4845,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(expected, spi.canRestoreTo(mService, pi, true));
}
- public void testCanRestoreTo() {
+ public void CanRestoreTo() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -4908,7 +4908,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1");
}
- public void testHandlePackageDelete() {
+ public void HandlePackageDelete() {
checkHandlePackageDeleteInner((userId, packageName) -> {
uninstallPackage(userId, packageName);
mService.mPackageMonitor.onReceive(getTestContext(),
@@ -4916,7 +4916,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHandlePackageDisable() {
+ public void HandlePackageDisable() {
checkHandlePackageDeleteInner((userId, packageName) -> {
disablePackage(userId, packageName);
mService.mPackageMonitor.onReceive(getTestContext(),
@@ -5048,7 +5048,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
/** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
- public void testHandlePackageClearData() {
+ public void HandlePackageClearData() {
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
setCaller(CALLING_PACKAGE_1, USER_0);
@@ -5124,7 +5124,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
}
- public void testHandlePackageClearData_manifestRepublished() {
+ public void HandlePackageClearData_manifestRepublished() {
mRunningUsers.put(USER_10, true);
@@ -5166,7 +5166,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHandlePackageUpdate() throws Throwable {
+ public void HandlePackageUpdate() throws Throwable {
// Set up shortcuts and launchers.
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -5340,7 +5340,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test the case where an updated app has resource IDs changed.
*/
- public void testHandlePackageUpdate_resIdChanged() throws Exception {
+ public void HandlePackageUpdate_resIdChanged() throws Exception {
final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
@@ -5415,7 +5415,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHandlePackageUpdate_systemAppUpdate() {
+ public void HandlePackageUpdate_systemAppUpdate() {
// Package1 is a system app. Package 2 is not a system app, so it's not scanned
// in this test at all.
@@ -5521,7 +5521,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint());
}
- public void testHandlePackageChanged() {
+ public void HandlePackageChanged() {
final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
@@ -5651,7 +5651,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable {
+ public void HandlePackageUpdate_activityNoLongerMain() throws Throwable {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithActivity("s1a",
@@ -5737,7 +5737,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* - Unpinned dynamic shortcuts
* - Bitmaps
*/
- public void testBackupAndRestore() {
+ public void BackupAndRestore() {
assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt");
assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml");
@@ -5758,7 +5758,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void testBackupAndRestore_backupRestoreTwice() {
+ public void BackupAndRestore_backupRestoreTwice() {
prepareForBackupTest();
checkBackupAndRestore_success(/*firstRestore=*/ true);
@@ -5774,7 +5774,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ false);
}
- public void testBackupAndRestore_restoreToNewVersion() {
+ public void BackupAndRestore_restoreToNewVersion() {
prepareForBackupTest();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
@@ -5783,7 +5783,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void testBackupAndRestore_restoreToSuperSetSignatures() {
+ public void BackupAndRestore_restoreToSuperSetSignatures() {
prepareForBackupTest();
// Change package signatures.
@@ -5980,7 +5980,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testBackupAndRestore_publisherWrongSignature() {
+ public void BackupAndRestore_publisherWrongSignature() {
prepareForBackupTest();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
@@ -5988,7 +5988,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH);
}
- public void testBackupAndRestore_publisherNoLongerBackupTarget() {
+ public void BackupAndRestore_publisherNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(CALLING_PACKAGE_1,
@@ -6117,7 +6117,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testBackupAndRestore_launcherLowerVersion() {
+ public void BackupAndRestore_launcherLowerVersion() {
prepareForBackupTest();
addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
@@ -6126,7 +6126,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void testBackupAndRestore_launcherWrongSignature() {
+ public void BackupAndRestore_launcherWrongSignature() {
prepareForBackupTest();
addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
@@ -6134,7 +6134,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_launcherNotRestored(true);
}
- public void testBackupAndRestore_launcherNoLongerBackupTarget() {
+ public void BackupAndRestore_launcherNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(LAUNCHER_1,
@@ -6239,7 +6239,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+ public void BackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(CALLING_PACKAGE_1,
@@ -6337,7 +6337,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testBackupAndRestore_disabled() {
+ public void BackupAndRestore_disabled() {
prepareCrossProfileDataSet();
// Before doing backup & restore, disable s1.
@@ -6402,7 +6402,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testBackupAndRestore_manifestRePublished() {
+ public void BackupAndRestore_manifestRePublished() {
// Publish two manifest shortcuts.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -6493,7 +6493,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* logcat.
* - if it has allowBackup=false, we don't touch any of the existing shortcuts.
*/
- public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
+ public void BackupAndRestore_appAlreadyInstalledWhenRestored() {
// Pre-backup. Same as testBackupAndRestore_manifestRePublished().
// Publish two manifest shortcuts.
@@ -6618,7 +6618,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for restoring the pre-P backup format.
*/
- public void testBackupAndRestore_api27format() throws Exception {
+ public void BackupAndRestore_api27format() throws Exception {
final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222");
@@ -6656,7 +6656,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testSaveAndLoad_crossProfile() {
+ public void SaveAndLoad_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -6859,7 +6859,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.getPackageUserId());
}
- public void testOnApplicationActive_permission() {
+ public void OnApplicationActive_permission() {
assertExpectException(SecurityException.class, "Missing permission", () ->
mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0));
@@ -6868,7 +6868,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0);
}
- public void testGetShareTargets_permission() {
+ public void GetShareTargets_permission() {
addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1");
mInjectedChooserActivity =
ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity");
@@ -6887,7 +6887,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testHasShareTargets_permission() {
+ public void HasShareTargets_permission() {
assertExpectException(SecurityException.class, "Missing permission", () ->
mManager.hasShareTargets(CALLING_PACKAGE_1));
@@ -6896,7 +6896,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.hasShareTargets(CALLING_PACKAGE_1);
}
- public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
+ public void isSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
setCaller(LAUNCHER_1, USER_0);
IntentFilter filter_any = new IntentFilter();
@@ -6911,18 +6911,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.hasShareTargets(CALLING_PACKAGE_1);
}
- public void testDumpsys_crossProfile() {
+ public void Dumpsys_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("test1", /* force= */ true);
}
- public void testDumpsys_withIcons() throws IOException {
- testIcons();
+ public void Dumpsys_withIcons() throws IOException {
+ Icons();
// Dump after having some icons.
dumpsysOnLogcat("test1", /* force= */ true);
}
- public void testManifestShortcut_publishOnUnlockUser() {
+ public void ManifestShortcut_publishOnUnlockUser() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
@@ -7136,7 +7136,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_0));
}
- public void testManifestShortcut_publishOnBroadcast() {
+ public void ManifestShortcut_publishOnBroadcast() {
// First, no packages are installed.
uninstallPackage(USER_0, CALLING_PACKAGE_1);
uninstallPackage(USER_0, CALLING_PACKAGE_2);
@@ -7392,7 +7392,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_missingMandatoryFields() {
+ public void ManifestShortcuts_missingMandatoryFields() {
// Start with no apps installed.
uninstallPackage(USER_0, CALLING_PACKAGE_1);
uninstallPackage(USER_0, CALLING_PACKAGE_2);
@@ -7461,7 +7461,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_intentDefinitions() {
+ public void ManifestShortcuts_intentDefinitions() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_4);
@@ -7603,7 +7603,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_checkAllFields() {
+ public void ManifestShortcuts_checkAllFields() {
mService.handleUnlockUser(USER_0);
// Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7708,7 +7708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_localeChange() throws InterruptedException {
+ public void ManifestShortcuts_localeChange() throws InterruptedException {
mService.handleUnlockUser(USER_0);
// Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7812,7 +7812,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_updateAndDisabled_notPinned() {
+ public void ManifestShortcuts_updateAndDisabled_notPinned() {
mService.handleUnlockUser(USER_0);
// First, just publish a manifest shortcut.
@@ -7852,7 +7852,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_updateAndDisabled_pinned() {
+ public void ManifestShortcuts_updateAndDisabled_pinned() {
mService.handleUnlockUser(USER_0);
// First, just publish a manifest shortcut.
@@ -7908,7 +7908,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_duplicateInSingleActivity() {
+ public void ManifestShortcuts_duplicateInSingleActivity() {
mService.handleUnlockUser(USER_0);
// The XML has two shortcuts with the same ID.
@@ -7933,7 +7933,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testManifestShortcuts_duplicateInTwoActivities() {
+ public void ManifestShortcuts_duplicateInTwoActivities() {
mService.handleUnlockUser(USER_0);
// ShortcutActivity has shortcut ms1
@@ -7985,7 +7985,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Manifest shortcuts cannot override shortcuts that were published via the APIs.
*/
- public void testManifestShortcuts_cannotOverrideNonManifest() {
+ public void ManifestShortcuts_cannotOverrideNonManifest() {
mService.handleUnlockUser(USER_0);
// Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
@@ -8058,7 +8058,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Make sure the APIs won't work on manifest shortcuts.
*/
- public void testManifestShortcuts_immutable() {
+ public void ManifestShortcuts_immutable() {
mService.handleUnlockUser(USER_0);
// Create a non-pinned manifest shortcut, a pinned shortcut that was originally
@@ -8151,7 +8151,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Make sure the APIs won't work on manifest shortcuts.
*/
- public void testManifestShortcuts_tooMany() {
+ public void ManifestShortcuts_tooMany() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8170,7 +8170,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testMaxShortcutCount_set() {
+ public void MaxShortcutCount_set() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8251,7 +8251,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testMaxShortcutCount_add() {
+ public void MaxShortcutCount_add() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8378,7 +8378,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testMaxShortcutCount_update() {
+ public void MaxShortcutCount_update() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8469,7 +8469,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutsPushedOutByManifest() {
+ public void ShortcutsPushedOutByManifest() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8577,7 +8577,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testReturnedByServer() {
+ public void ReturnedByServer() {
// Package 1 updated, with manifest shortcuts.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8623,7 +8623,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testIsForegroundDefaultLauncher_true() {
+ public void IsForegroundDefaultLauncher_true() {
final int uid = 1024;
setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8633,7 +8633,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testIsForegroundDefaultLauncher_defaultButNotForeground() {
+ public void IsForegroundDefaultLauncher_defaultButNotForeground() {
final int uid = 1024;
setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8642,7 +8642,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(mInternal.isForegroundDefaultLauncher("default", uid));
}
- public void testIsForegroundDefaultLauncher_foregroundButNotDefault() {
+ public void IsForegroundDefaultLauncher_foregroundButNotDefault() {
final int uid = 1024;
setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8651,7 +8651,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
}
- public void testParseShareTargetsFromManifest() {
+ public void ParseShareTargetsFromManifest() {
// These values must exactly match the content of shortcuts_share_targets.xml resource
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
@@ -8703,7 +8703,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+ public void ShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8769,7 +8769,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
+ public void IsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_share_targets);
@@ -8819,7 +8819,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
filter_any));
}
- public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts()
+ public void IsSharingShortcut_PinnedAndCachedOnlyShortcuts()
throws IntentFilter.MalformedMimeTypeException {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8876,7 +8876,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
filter_any));
}
- public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void AddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1");
final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2");
final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3");
@@ -8897,7 +8897,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void UpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcut("s1");
final ShortcutInfo s2 = makeShortcut("s2");
final ShortcutInfo s3 = makeShortcut("s3");
@@ -8910,7 +8910,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testPinHiddenShortcuts_ThrowsException() {
+ public void PinHiddenShortcuts_ThrowsException() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertThrown(IllegalArgumentException.class, () -> {
mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
index d69e47684f8b..9b878b349618 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.LocaleManager;
@@ -26,6 +27,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.multiuser.Flags;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -261,6 +263,162 @@ public final class UserManagerCacheTest {
assertThat(um.getUserName()).isEqualTo(newName);
}
+
+ @MediumTest
+ @Test
+ public void testDefaultRestrictionsApplied() throws Exception {
+ final UserInfo userInfo = mUserManager.createUser("Useroid",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0);
+ mUsersToRemove.add(userInfo.id);
+ final UserTypeDetails userTypeDetails =
+ UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_FULL_SECONDARY);
+ final Bundle expectedRestrictions = userTypeDetails.getDefaultRestrictions();
+ // Note this can fail if DO unset those restrictions.
+ for (String restriction : expectedRestrictions.keySet()) {
+ if (expectedRestrictions.getBoolean(restriction)) {
+ assertThat(mUserManager.hasUserRestriction(restriction, UserHandle.of(userInfo.id)))
+ .isTrue();
+ // Test cached value
+ assertThat(mUserManager.hasUserRestriction(restriction, UserHandle.of(userInfo.id)))
+ .isTrue();
+ }
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testSetDefaultGuestRestrictions() {
+ final Bundle origRestrictions = mUserManager.getDefaultGuestRestrictions();
+ try {
+ final boolean isFunDisallowed = origRestrictions.getBoolean(UserManager.DISALLOW_FUN,
+ false);
+ final UserInfo guest1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ assertThat(guest1).isNotNull();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest1.getUserHandle())).isEqualTo(isFunDisallowed);
+ removeUser(guest1.id, true);
+ // Cache return false after user was removed
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest1.getUserHandle())).isFalse();
+
+ Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_FUN, !isFunDisallowed);
+ mUserManager.setDefaultGuestRestrictions(restrictions);
+ UserInfo guest2 = mUserManager.createUser("Guest 2", UserInfo.FLAG_GUEST);
+ assertThat(guest2).isNotNull();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest2.getUserHandle())).isNotEqualTo(isFunDisallowed);
+ removeUser(guest2.id, true);
+ assertThat(mUserManager.getUserInfo(guest2.id)).isNull();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest2.getUserHandle())).isFalse();
+ } finally {
+ mUserManager.setDefaultGuestRestrictions(origRestrictions);
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testCacheInvalidatedAfterUserAddedOrRemoved() {
+ final Bundle origRestrictions = mUserManager.getDefaultGuestRestrictions();
+ try {
+ final boolean isFunDisallowed = origRestrictions.getBoolean(UserManager.DISALLOW_FUN,
+ false);
+ final UserInfo guest1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ assertThat(guest1).isNotNull();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest1.getUserHandle())).isEqualTo(isFunDisallowed);
+ removeUser(guest1.id, true);
+
+ Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_FUN, !isFunDisallowed);
+ mUserManager.setDefaultGuestRestrictions(restrictions);
+ int latest_id = guest1.id;
+ // Cache removed id and few next ids.
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ UserHandle.of(latest_id))).isFalse();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ UserHandle.of(latest_id + 1))).isFalse();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ UserHandle.of(latest_id + 2))).isFalse();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ UserHandle.of(latest_id + 3))).isFalse();
+
+ UserInfo guest2 = mUserManager.createUser("Guest 2", UserInfo.FLAG_GUEST);
+ assertThat(guest2).isNotNull();
+ // Cache was invalidated after user was added
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest2.getUserHandle())).isTrue();
+ removeUser(guest2.id, true);
+ assertThat(mUserManager.getUserInfo(guest2.id)).isNull();
+ // Cache was invalidated after user was removed
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ guest2.getUserHandle())).isFalse();
+ } finally {
+ mUserManager.setDefaultGuestRestrictions(origRestrictions);
+ }
+ }
+
+
+ @MediumTest
+ @Test
+ public void testAddRemoveUsersAndRestrictions() {
+ try {
+ final UserInfo userInfo = mUserManager.createUser("Useroid",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0);
+ mUsersToRemove.add(userInfo.id);
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ userInfo.getUserHandle())).isFalse();
+ mUserManager.setUserRestriction(UserManager.DISALLOW_FUN, true,
+ userInfo.getUserHandle());
+
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ userInfo.getUserHandle())).isTrue();
+ removeUser(userInfo.id, true);
+ assertThat(mUserManager.getUserSerialNumber(userInfo.id)).isEqualTo(-1);
+ assertThat(mUserManager.getUserInfo(userInfo.id)).isNull();
+ assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+ userInfo.getUserHandle())).isFalse();
+ } catch (java.lang.Exception e) {
+ }
+ }
+
+
+ private void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @MediumTest
+ @Test
+ public void testDefaultUserRestrictionsForPrivateProfile() {
+ assumeTrue(mUserManager.canAddPrivateProfile());
+ final int currentUserId = ActivityManager.getCurrentUser();
+ UserInfo privateProfileInfo = null;
+ try {
+ privateProfileInfo = mUserManager.createProfileForUser(
+ "Private", UserManager.USER_TYPE_PROFILE_PRIVATE, 0, currentUserId, null);
+ assertThat(privateProfileInfo).isNotNull();
+ } catch (Exception e) {
+ fail("Creation of private profile failed due to " + e.getMessage());
+ }
+ assertDefaultPrivateProfileRestrictions(privateProfileInfo.getUserHandle());
+ // Assert cached values
+ assertDefaultPrivateProfileRestrictions(privateProfileInfo.getUserHandle());
+ }
+
+ private void assertDefaultPrivateProfileRestrictions(UserHandle userHandle) {
+ Bundle defaultPrivateProfileRestrictions =
+ UserTypeFactory.getDefaultPrivateProfileRestrictions();
+ for (String restriction : defaultPrivateProfileRestrictions.keySet()) {
+ assertThat(mUserManager.hasUserRestrictionForUser(restriction, userHandle)).isTrue();
+ }
+ }
+
private void assumeManagedUsersSupported() {
// In Automotive, if headless system user is enabled, a managed user cannot be created
// under a primary user.
@@ -270,9 +428,23 @@ public final class UserManagerCacheTest {
}
private void removeUser(int userId) {
+ removeUser(userId, false);
+ }
+
+ private void removeUser(int userId, boolean waitForCompleteRemoval) {
mUserManager.removeUser(userId);
mUserRemovalWaiter.waitFor(userId);
mUsersToRemove.remove(userId);
+ if (waitForCompleteRemoval) {
+ int serialNumber = mUserManager.getUserSerialNumber(userId);
+ int timeout = REMOVE_USER_TIMEOUT_SECONDS * 5; // called every 200ms
+ // Wait for the user to be removed from memory
+ while (serialNumber > 0 && timeout > 0) {
+ sleep(200);
+ timeout--;
+ serialNumber = mUserManager.getUserSerialNumber(userId);
+ }
+ }
}
private boolean isAutomotive() {
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 a8544f60fb74..5862ac65eba9 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -26,6 +26,7 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.os.Handler
import android.os.PersistableBundle
@@ -59,6 +60,7 @@ class SupervisionServiceTest {
@get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
+ @Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
private lateinit var context: Context
@@ -68,7 +70,7 @@ class SupervisionServiceTest {
@Before
fun setUp() {
context = InstrumentationRegistry.getInstrumentation().context
- context = SupervisionContextWrapper(context)
+ context = SupervisionContextWrapper(context, mockPackageManager)
LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -137,6 +139,31 @@ class SupervisionServiceTest {
}
@Test
+ fun isActiveSupervisionApp_supervisionUid_supervisionEnabled_returnsTrue() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(arrayOf(systemSupervisionPackage))
+ service.setSupervisionEnabledForUser(USER_ID, true)
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isTrue()
+ }
+
+ @Test
+ fun isActiveSupervisionApp_supervisionUid_supervisionNotEnabled_returnsFalse() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(arrayOf(systemSupervisionPackage))
+ service.setSupervisionEnabledForUser(USER_ID, false)
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
+ }
+
+ @Test
+ fun isActiveSupervisionApp_notSupervisionUid_returnsFalse() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn(arrayOf())
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
+ }
+
+ @Test
fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
@@ -191,6 +218,7 @@ class SupervisionServiceTest {
private companion object {
const val USER_ID = 100
+ val APP_UID = USER_ID * UserHandle.PER_USER_RANGE
}
}
@@ -198,9 +226,12 @@ 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) : ContextWrapper(context) {
+private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) :
+ ContextWrapper(context) {
val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()
+ override fun getPackageManager() = pkgManager
+
override fun registerReceiverForAllUsers(
receiver: BroadcastReceiver?,
filter: IntentFilter,
diff --git a/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java b/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java
new file mode 100644
index 000000000000..a2df73b7d540
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.server.utils;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class LazyJniRegistrarTest {
+
+ @Test
+ public void testNativeMethodsResolve() throws Exception {
+ // Basic test with a few explicit invocations to make sure methods resolve and don't throw.
+ LazyJniRegistrar.registerConsumerIrService();
+ LazyJniRegistrar.registerGameManagerService();
+ LazyJniRegistrar.registerVrManagerService();
+ }
+
+ @Test
+ public void testAllNativeRegisterMethodsResolve() throws Exception {
+ // Catch-all test to make sure public static register* methods resolve and don't throw.
+ for (Method method : LazyJniRegistrar.class.getDeclaredMethods()) {
+ if (Modifier.isPublic(method.getModifiers())
+ && Modifier.isStatic(method.getModifiers())
+ && method.getName().startsWith("register")) {
+ method.invoke(null);
+ }
+ }
+ }
+
+ // TODO(b/302724778): Remove manual JNI load
+ static {
+ System.loadLibrary("servicestestjni");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
index f5b19a1c40ae..69b9fa23c040 100644
--- a/services/tests/servicestests/src/com/android/server/utils/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -1,5 +1,6 @@
per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLoggerTest.java = jmtrivi@google.com
+per-file LazyJniRegistrarTest.java = file:/PERFORMANCE_OWNERS
# Bug component : 158088 = per-file AnrTimer*.java
per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 0404b82d306d..c4b8599a483c 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -118,6 +118,7 @@ import org.mockito.Spy;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -232,7 +233,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
any(AlarmManager.OnAlarmListener.class), any(Handler.class));
doAnswer(inv -> {
- mCustomListener = () -> {};
+ mCustomListener = () -> {
+ };
return null;
}).when(mAlarmManager).cancel(eq(mCustomListener));
when(mContext.getSystemService(eq(Context.POWER_SERVICE)))
@@ -1321,7 +1323,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void enableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.enableCarMode(0, 0, PACKAGE_NAME));
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
@@ -1343,19 +1345,19 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void disableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.enableCarMode(0, 0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class,
- () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
+ () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
// Clean up
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
}
@@ -1473,12 +1475,13 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
}
- // attention modes with expected night modes
- Map<Integer, Boolean> modes = Map.of(
- MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode,
- MODE_ATTENTION_THEME_OVERLAY_DAY, false,
- MODE_ATTENTION_THEME_OVERLAY_NIGHT, true
- );
+ // Attention modes with expected night modes.
+ // Important to keep modes.put(MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode) in the
+ // first position, hence LinkedHashMap.
+ Map<Integer, Boolean> modes = new LinkedHashMap<>();
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode);
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_DAY, false);
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_NIGHT, true);
// test
for (int attentionMode : modes.keySet()) {
@@ -1562,7 +1565,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
private final int callingUid;
public TestInjector() {
- this(DEFAULT_CALLING_UID);
+ this(DEFAULT_CALLING_UID);
}
public TestInjector(int callingUid) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
index fa1372d9f4ef..87b9154cfb4d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -88,6 +88,8 @@ public class EventConditionProviderTest extends UiServiceTestCase {
mService.mContext = this.getContext();
mContext.addMockSystemService(UserManager.class, mUserManager);
+ when(mUserManager.getProfiles(eq(UserHandle.USER_SYSTEM))).thenReturn(
+ List.of(new UserInfo(UserHandle.USER_SYSTEM, "USER_SYSTEM", 0)));
when(mUserManager.getProfiles(eq(mUserId))).thenReturn(
List.of(new UserInfo(mUserId, "mUserId", 0)));
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 90bf1d36a4ce..1fc0d245fbe5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -49,10 +49,14 @@ import static android.app.NotificationChannel.PROMOTIONS_ID;
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -307,6 +311,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.config.sysui.TestableFlagResolver;
+import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.internal.messages.nano.SystemMessageProto;
@@ -369,6 +374,7 @@ import java.io.FileOutputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -7632,7 +7638,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mTestNotificationChannel, 1, null, true));
when(r1.getLifespanMs(anyLong())).thenReturn(234);
- r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ InstanceId instanceId1 = mNotificationInstanceIdSequence.newInstanceId();
+ r1.getSbn().setInstanceId(instanceId1);
// Enqueues the notification to be posted, so hasPosted will be false.
mService.addEnqueuedNotification(r1);
@@ -7643,8 +7650,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r1.getSbn().getPackageName(), r1.getKey(), signals, "",
r1.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment1);
- assertTrue(mService.checkLastClassificationChannelLog(false /*hasPosted*/,
- true /*isAlerting*/, 3 /*TYPE_NEWS*/, 234));
+ assertTrue(mService.checkLastClassificationChannelLog(false /*=hasPosted*/,
+ true /*=isAlerting*/, Adjustment.TYPE_NEWS, 234,
+ NOTIFICATION_ADJUSTED.getId(),
+ instanceId1.getId(), r1.getUid()));
// Set up notifications that will be adjusted
// This notification starts on a low importance channel, so isAlerting is false.
@@ -7654,7 +7663,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mLowImportanceNotificationChannel, 1, null, true));
when(r2.getLifespanMs(anyLong())).thenReturn(345);
- r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ InstanceId instanceId2 = mNotificationInstanceIdSequence.newInstanceId();
+ r2.getSbn().setInstanceId(instanceId2);
// Adds the notification as already posted, so hasPosted will be true.
mService.addNotification(r2);
// The signal is removed when used so it has to be readded.
@@ -7664,15 +7674,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r2.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment2);
assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
- false /*isAlerting*/, 3 /*TYPE_NEWS*/, 345)); // currently failing
+ false /*isAlerting*/, Adjustment.TYPE_NEWS, 345,
+ NOTIFICATION_ADJUSTED.getId(),
+ instanceId2.getId() /*instance_id*/, r2.getUid()));
signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_PROMOTION);
Adjustment adjustment3 = new Adjustment(
r2.getSbn().getPackageName(), r2.getKey(), signals, "",
r2.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment3);
- assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
- false /*isAlerting*/, 1 /*TYPE_PROMOTION*/, 345));
+ assertTrue(mService.checkLastClassificationChannelLog(true /*=hasPosted*/,
+ false /*=isAlerting*/, Adjustment.TYPE_PROMOTION, 345,
+ NOTIFICATION_ADJUSTED.getId(),
+ instanceId2.getId() /*instance_id*/, r2.getUid()));
}
@Test
@@ -11273,19 +11287,71 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(102)));
}
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception {
+ mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged(
+ mUserId, "rule.owner.pkg", "rule_id", AUTOMATIC_RULE_STATUS_ACTIVATED));
+
+ Intent expected = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED)
+ .setPackage("rule.owner.pkg")
+ .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, "rule_id")
+ .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS, AUTOMATIC_RULE_STATUS_ACTIVATED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
+ }
+
private static Intent eqIntent(Intent wanted) {
return ArgumentMatchers.argThat(
new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
return wanted.filterEquals(argument)
- && wanted.getFlags() == argument.getFlags();
+ && wanted.getFlags() == argument.getFlags()
+ && equalBundles(wanted.getExtras(), argument.getExtras());
}
@Override
public String toString() {
return wanted.toString();
}
+
+ private boolean equalBundles(Bundle one, Bundle two) {
+ if (one == null && two == null) {
+ return true;
+ }
+ if ((one == null) != (two == null)) {
+ return false;
+ }
+ if (one.size() != two.size()) {
+ return false;
+ }
+
+ HashSet<String> setOne = new HashSet<>(one.keySet());
+ setOne.addAll(two.keySet());
+
+ for (String key : setOne) {
+ if (!one.containsKey(key) || !two.containsKey(key)) {
+ return false;
+ }
+
+ Object valueOne = one.get(key);
+ Object valueTwo = two.get(key);
+ if (valueOne instanceof Bundle
+ && valueTwo instanceof Bundle
+ && !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
+ return false;
+ } else if (valueOne == null) {
+ if (valueTwo != null) {
+ return false;
+ }
+ } else if (!valueOne.equals(valueTwo)) {
+ return false;
+ }
+ }
+ return true;
+ }
});
}
@@ -17314,6 +17380,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17327,6 +17394,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
StatusBarNotification sbn1 = new StatusBarNotification(mPkg, mPkg, 7, null, mUid, 0,
@@ -17339,6 +17407,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
StatusBarNotification sbn2 = new StatusBarNotification(PKG_O, PKG_O, 7, null, UID_O, 0,
@@ -17388,6 +17457,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17420,6 +17490,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17434,6 +17505,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17483,6 +17555,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17515,6 +17588,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -17543,6 +17617,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
@@ -17570,6 +17645,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
index 65ed7b6e622d..934c33b7f1f1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.HOURS;
+import android.service.notification.RateEstimator;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index ba91ca2323af..b4348194060a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
@@ -25,6 +27,7 @@ import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceIdSequence;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
import java.util.HashSet;
import java.util.Set;
@@ -57,6 +60,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
public boolean isAlerting;
public long classification;
public long lifetime;
+ public long eventId;
+ public long instanceId;
+ public long uid;
}
public ClassificationChannelLog lastClassificationChannelLog = null;
@@ -221,20 +227,34 @@ public class TestableNotificationManagerService extends NotificationManagerServi
}
@Override
- protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
- int classification, int lifetimeMs) {
+ protected void logClassificationChannelAdjustmentReceived(NotificationRecord r,
+ boolean hasPosted,
+ int classification) {
+
+ boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+ int instanceId = r.getSbn().getInstanceId() == null
+ ? 0 : r.getSbn().getInstanceId().getId();
+ int lifetimeMs = r.getLifespanMs(System.currentTimeMillis());
+ int uid = r.getUid();
+
lastClassificationChannelLog = new ClassificationChannelLog();
lastClassificationChannelLog.hasPosted = hasPosted;
lastClassificationChannelLog.isAlerting = isAlerting;
lastClassificationChannelLog.classification = classification;
lastClassificationChannelLog.lifetime = lifetimeMs;
+ lastClassificationChannelLog.eventId =
+ NotificationReportedEvent.NOTIFICATION_ADJUSTED.getId();
+ lastClassificationChannelLog.instanceId = instanceId;
+ lastClassificationChannelLog.uid = uid;
}
/**
* Returns true if the last recorded classification channel log has all the values specified.
*/
public boolean checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting,
- int classification, int lifetime) {
+ int classification, int lifetime,
+ int eventId, int instanceId,
+ int uid) {
if (lastClassificationChannelLog == null) {
return false;
}
@@ -242,6 +262,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
return hasPosted == lastClassificationChannelLog.hasPosted
&& isAlerting == lastClassificationChannelLog.isAlerting
&& classification == lastClassificationChannelLog.classification
- && lifetime == lastClassificationChannelLog.lifetime;
+ && lifetime == lastClassificationChannelLog.lifetime
+ && eventId == lastClassificationChannelLog.eventId
+ && instanceId == lastClassificationChannelLog.instanceId
+ && uid == lastClassificationChannelLog.uid;
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
index 3ac78908d8ae..af911e811e5e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -49,18 +49,22 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase {
@Test
public void builder() {
- ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
- .setShouldDimWallpaper(true)
- .setShouldDisableTapToWake(true).setShouldDisableTapToWake(false)
- .setShouldDisableTiltToWake(true)
- .setShouldMaximizeDoze(true)
- .setShouldUseNightMode(false)
- .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
- .addExtraEffect("WILL BE GONE")
- .setExtraEffects(ImmutableSet.of("1", "2"))
- .addExtraEffects(ImmutableSet.of("3", "4"))
- .addExtraEffect("5")
- .build();
+ ZenDeviceEffects deviceEffects =
+ new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTapToWake(false)
+ .setShouldDisableTiltToWake(true)
+ .setShouldMaximizeDoze(true)
+ .setShouldUseNightMode(false)
+ .setShouldUseNightLight(true)
+ .setShouldSuppressAmbientDisplay(false)
+ .setShouldSuppressAmbientDisplay(true)
+ .addExtraEffect("WILL BE GONE")
+ .setExtraEffects(ImmutableSet.of("1", "2"))
+ .addExtraEffects(ImmutableSet.of("3", "4"))
+ .addExtraEffect("5")
+ .build();
assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
assertThat(deviceEffects.shouldDisableAutoBrightness()).isFalse();
@@ -68,6 +72,7 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase {
assertThat(deviceEffects.shouldDisableTiltToWake()).isTrue();
assertThat(deviceEffects.shouldDisableTouch()).isFalse();
assertThat(deviceEffects.shouldDisplayGrayscale()).isFalse();
+ assertThat(deviceEffects.shouldUseNightLight()).isTrue();
assertThat(deviceEffects.shouldMaximizeDoze()).isTrue();
assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
assertThat(deviceEffects.shouldUseNightMode()).isFalse();
@@ -85,15 +90,18 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase {
.addExtraEffect("1")
.build();
- ZenDeviceEffects modified = new ZenDeviceEffects.Builder(original)
- .setShouldDisplayGrayscale(true)
- .setShouldUseNightMode(false)
- .addExtraEffect("2")
- .build();
+ ZenDeviceEffects modified =
+ new ZenDeviceEffects.Builder(original)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(false)
+ .setShouldUseNightLight(true)
+ .addExtraEffect("2")
+ .build();
assertThat(modified.shouldDimWallpaper()).isTrue(); // from original
assertThat(modified.shouldDisableTiltToWake()).isTrue(); // from original
assertThat(modified.shouldDisplayGrayscale()).isTrue(); // updated
+ assertThat(modified.shouldUseNightLight()).isTrue(); // updated
assertThat(modified.shouldUseNightMode()).isFalse(); // updated
assertThat(modified.shouldSuppressAmbientDisplay()).isTrue(); // from original
assertThat(modified.getExtraEffects()).containsExactly("1", "2"); // updated
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 3236f9501324..b42a6a5a7382 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -18,7 +18,6 @@ package com.android.server.notification;
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
-import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.Flags.modesUi;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -26,7 +25,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.suppressedEffectsToString;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
@@ -56,9 +54,7 @@ import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -71,7 +67,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Parcel;
-import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
@@ -100,6 +95,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -108,9 +106,6 @@ import java.io.IOException;
import java.time.Instant;
import java.util.List;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
public class ZenModeConfigTest extends UiServiceTestCase {
@@ -731,19 +726,21 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
- rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
- .setShouldDisplayGrayscale(false)
- .setShouldSuppressAmbientDisplay(true)
- .setShouldDimWallpaper(false)
- .setShouldUseNightMode(true)
- .setShouldDisableAutoBrightness(false)
- .setShouldDisableTapToWake(true)
- .setShouldDisableTiltToWake(false)
- .setShouldDisableTouch(true)
- .setShouldMinimizeRadioUsage(false)
- .setShouldMaximizeDoze(true)
- .setExtraEffects(ImmutableSet.of("one", "two"))
- .build();
+ rule.zenDeviceEffects =
+ new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(false)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(false)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(false)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(false)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(false)
+ .setShouldMaximizeDoze(true)
+ .setShouldUseNightLight(true)
+ .setExtraEffects(ImmutableSet.of("one", "two"))
+ .build();
rule.creationTime = CREATION_TIME;
rule.allowManualInvocation = ALLOW_MANUAL;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index c6cc941ba1cd..b138c72875a6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -54,6 +54,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -67,10 +70,6 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
@@ -232,6 +231,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
+ "mDisableTouch:true->false, "
+ "mMinimizeRadioUsage:true->false, "
+ "mMaximizeDoze:true->false, "
+ + "mNightLight:true->false, "
+ "mExtraEffects:[effect1]->[effect2]}, "
+ "triggerDescription:string1->string2, "
+ "type:2->1, "
@@ -358,18 +358,21 @@ public class ZenModeDiffTest extends UiServiceTestCase {
generateFieldDiffs(effects1, effects2, fieldsForDiff, expectedFrom, expectedTo);
d = new ZenModeDiff.DeviceEffectsDiff(effects1, effects2);
- assertThat(d.toString()).isEqualTo("ZenDeviceEffectsDiff{"
- + "mGrayscale:true->false, "
- + "mSuppressAmbientDisplay:true->false, "
- + "mDimWallpaper:true->false, "
- + "mNightMode:true->false, "
- + "mDisableAutoBrightness:true->false, "
- + "mDisableTapToWake:true->false, "
- + "mDisableTiltToWake:true->false, "
- + "mDisableTouch:true->false, "
- + "mMinimizeRadioUsage:true->false, "
- + "mMaximizeDoze:true->false, "
- + "mExtraEffects:[effect1]->[effect2]}");
+ assertThat(d.toString())
+ .isEqualTo(
+ "ZenDeviceEffectsDiff{"
+ + "mGrayscale:true->false, "
+ + "mSuppressAmbientDisplay:true->false, "
+ + "mDimWallpaper:true->false, "
+ + "mNightMode:true->false, "
+ + "mDisableAutoBrightness:true->false, "
+ + "mDisableTapToWake:true->false, "
+ + "mDisableTiltToWake:true->false, "
+ + "mDisableTouch:true->false, "
+ + "mMinimizeRadioUsage:true->false, "
+ + "mMaximizeDoze:true->false, "
+ + "mNightLight:true->false, "
+ + "mExtraEffects:[effect1]->[effect2]}");
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 09da0156eb82..1884bbd39bb9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -202,6 +202,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -221,9 +224,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
@@ -2759,18 +2759,20 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
- ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
- .setShouldDisplayGrayscale(true)
- .setShouldSuppressAmbientDisplay(true)
- .setShouldDimWallpaper(true)
- .setShouldUseNightMode(true)
- .setShouldDisableAutoBrightness(true)
- .setShouldDisableTapToWake(true)
- .setShouldDisableTiltToWake(true)
- .setShouldDisableTouch(true)
- .setShouldMinimizeRadioUsage(true)
- .setShouldMaximizeDoze(true)
- .build();
+ ZenDeviceEffects zde =
+ new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(true)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldMaximizeDoze(true)
+ .setShouldUseNightLight(true)
+ .build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(),
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 787f4e85c012..cbbbc731753d 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -22,7 +22,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -30,21 +30,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
@@ -56,7 +48,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META|SHIFT" />
<bookmark
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 1e9038ee1769..9d4d94bebfd9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -26,12 +26,12 @@ import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAV
import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
-import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
import android.view.KeyEvent;
import androidx.test.filters.MediumTest;
@@ -258,9 +258,9 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
{"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
KeyEvent.KEYCODE_EXPLORER, 0},
- {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+ {"Meta + P -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_P},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
- KeyEvent.KEYCODE_C, META_ON},
+ KeyEvent.KEYCODE_P, META_ON},
{"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
KeyEvent.KEYCODE_CONTACTS, 0},
@@ -270,15 +270,12 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
{"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
KeyEvent.KEYCODE_ENVELOPE, 0},
- {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+ {"Meta + C -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_C},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
- KeyEvent.KEYCODE_K, META_ON},
+ KeyEvent.KEYCODE_C, META_ON},
{"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
KeyEvent.KEYCODE_CALENDAR, 0},
- {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
- KeyEvent.KEYCODE_P, META_ON},
{"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
KeyEvent.KEYCODE_MUSIC, 0},
@@ -291,11 +288,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
KeyEvent.KEYCODE_CALCULATOR, 0},
{"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
- KeyEvent.KEYCODE_M, META_ON},
- {"Meta + S -> Launch Default Messaging App",
- new int[]{META_KEY, KeyEvent.KEYCODE_S},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
- KeyEvent.KEYCODE_S, META_ON}};
+ KeyEvent.KEYCODE_M, META_ON}};
}
@Keep
@@ -468,6 +461,14 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
+ public void testKeyGestureLaunchVoiceAssistant() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT));
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ }
+
+ @Test
public void testKeyGestureGoHome() {
Assert.assertTrue(
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME));
@@ -757,54 +758,57 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG})
- public void testKeyGestureToggleStickyKeys() {
+ public void testKeyGestureToggleDoNotDisturb() {
+ mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_OFF);
Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
- Assert.assertTrue(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+ 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_STICKY_KEYS));
- Assert.assertFalse(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+ sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB));
+ mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_OFF);
}
@Test
- @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG})
- public void testKeyGestureToggleSlowKeys() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
- Assert.assertTrue(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
+ public void testLaunchSettingsAndSearchDoesntOpenAnything_withKeyguardOn() {
+ mPhoneWindowManager.overrideKeyguardOn(true);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
- Assert.assertFalse(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
+
+ mPhoneWindowManager.assertNoActivityLaunched();
}
@Test
- @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS})
- public void testKeyGestureToggleMouseKeys() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
- Assert.assertTrue(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
+ public void testLaunchSettingsAndSearchDoesntOpenAnything_withUserSetupIncomplete() {
+ mPhoneWindowManager.overrideIsUserSetupComplete(false);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
- Assert.assertFalse(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
+
+ mPhoneWindowManager.assertNoActivityLaunched();
}
@Test
- @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG})
- public void testKeyGestureToggleBounceKeys() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
- Assert.assertTrue(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
+ public void testLaunchAssistantDoesntWork_withKeyguardOn() {
+ mPhoneWindowManager.overrideKeyguardOn(true);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
- Assert.assertFalse(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
+
+ mPhoneWindowManager.assertSearchManagerDoesntLaunchAssist();
+ }
+
+ @Test
+ public void testLaunchAssistantDoesntWork_withUserSetupIncomplete() {
+ mPhoneWindowManager.overrideIsUserSetupComplete(false);
+
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
+
+ mPhoneWindowManager.assertSearchManagerDoesntLaunchAssist();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index cf5323e1f3a5..35b077e30f12 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -26,7 +26,6 @@ import static android.view.KeyEvent.KEYCODE_E;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.KeyEvent.KEYCODE_H;
import static android.view.KeyEvent.KEYCODE_J;
-import static android.view.KeyEvent.KEYCODE_K;
import static android.view.KeyEvent.KEYCODE_M;
import static android.view.KeyEvent.KEYCODE_META_LEFT;
import static android.view.KeyEvent.KEYCODE_N;
@@ -67,14 +66,12 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
// These shortcuts should align with those defined in
// services/tests/wmtests/res/xml/bookmarks.xml
INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR);
- INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
+ INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS);
INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
- INTENT_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR);
+ INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR);
INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
- INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER);
- ROLE_SHORTCUTS.append(KEYCODE_S, RoleManager.ROLE_SMS);
}
private static final int ANY_DISPLAY_ID = 123;
@@ -109,7 +106,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0);
mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER);
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_C}, 0);
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_P}, 0);
mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS);
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0);
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 9db76d47fed7..285d94d2fafe 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -447,6 +447,14 @@ class TestPhoneWindowManager {
mTestLooper.dispatchAll();
}
+ void overrideZenMode(int mode) {
+ doReturn(mode).when(mNotificationManager).getZenMode();
+ }
+
+ void assertZenMode(int mode) {
+ verify(mNotificationManager).setZenMode(eq(mode), any(), anyString(), eq(true));
+ }
+
/**
* Below functions will override the setting or the policy behavior.
*/
@@ -563,6 +571,10 @@ class TestPhoneWindowManager {
doNothing().when(mPhoneWindowManager).launchHomeFromHotKey(anyInt());
}
+ void overrideKeyguardOn(boolean isKeyguardOn) {
+ doReturn(isKeyguardOn).when(mPhoneWindowManager).keyguardOn();
+ }
+
void overrideIsUserSetupComplete(boolean isCompleted) {
doReturn(isCompleted).when(mPhoneWindowManager).isUserSetupComplete();
}
@@ -725,6 +737,11 @@ class TestPhoneWindowManager {
verify(mSearchManager).launchAssist(any());
}
+ void assertSearchManagerDoesntLaunchAssist() {
+ mTestLooper.dispatchAll();
+ verify(mSearchManager, never()).launchAssist(any());
+ }
+
void assertLaunchSystemSettings() {
mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -929,4 +946,10 @@ class TestPhoneWindowManager {
verify(mInputManagerInternal)
.handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
}
+
+ void assertNoActivityLaunched() {
+ mTestLooper.dispatchAll();
+ verify(mContext, never()).startActivityAsUser(any(), any(), any());
+ verify(mContext, never()).startActivityAsUser(any(), any());
+ }
}
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 d4a921c5f00a..c9cbe0fa08c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -32,6 +32,8 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -124,6 +126,7 @@ import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.WindowStateResizeItem;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -138,6 +141,7 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.util.MutableBoolean;
@@ -159,9 +163,13 @@ import com.android.internal.R;
import com.android.server.wm.ActivityRecord.State;
import com.android.window.flags.Flags;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -183,6 +191,9 @@ import java.util.function.Consumer;
@RunWith(WindowTestRunner.class)
public class ActivityRecordTests extends WindowTestsBase {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
private static final int ORIENTATION_CONFIG_CHANGES =
@@ -554,6 +565,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)
.setConfigChanges(ORIENTATION_CONFIG_CHANGES)
@@ -641,6 +653,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void ignoreRequestedOrientationForResizableInSplitWindows() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
@@ -685,6 +698,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void respectRequestedOrientationForNonResizableInSplitWindows() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
spyOn(tda);
doReturn(true).when(tda).supportsNonResizableMultiWindow();
@@ -718,6 +732,64 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOrientation_allowFixedOrientationForCameraCompatInFreeformWindowing() {
+ final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+ /* isCameraRunning= */ true, WINDOWING_MODE_FREEFORM);
+
+ // Task in landscape.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+ // The app should be letterboxed.
+ assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+ assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
+ public void testOrientation_dontAllowFixedOrientationForCameraCompatFreeformIfNotEnabled() {
+ final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+ /* isCameraRunning= */ true, WINDOWING_MODE_FREEFORM);
+
+ // Task in landscape.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+ // Activity is not letterboxed.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOrientation_noFixedOrientationForCameraCompatFreeformIfCameraNotRunning() {
+ final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+ /* isCameraRunning= */ false, WINDOWING_MODE_FREEFORM);
+
+ // Task in landscape.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+ // Activity is not letterboxed.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOrientation_dontAllowFixedOrientationForCameraCompatFreeformIfInPip() {
+ final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+ /* isCameraRunning= */ true, WINDOWING_MODE_PINNED);
+
+ // Task in landscape.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+ // Activity is not letterboxed.
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
public void testShouldMakeActive_deferredResume() {
final ActivityRecord activity = createActivityWithTask();
activity.setState(STOPPED, "Testing");
@@ -1906,6 +1978,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testActivityOnCancelFixedRotationTransform() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity = createActivityWithTask();
final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation();
final RemoteDisplayChangeController remoteDisplayChangeController = activity
@@ -2054,6 +2127,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testFixedRotationSnapshotStartingWindow() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity = createActivityWithTask();
// TaskSnapshotSurface requires a fullscreen opaque window.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
@@ -2278,6 +2352,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testSupportsFreeform() {
final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setComponent(getUniqueComponentName(mContext.getPackageName()))
.setCreateTask(true)
.setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
@@ -2410,6 +2485,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testOrientationForScreenOrientationBehind() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task task = createTask(mDisplayContent);
// Activity below
new ActivityBuilder(mAtm)
@@ -2465,11 +2541,14 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
assertEquals(0, activity.getChildCount());
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4");
+ final WindowState win1 = newWindowBuilder("app1", TYPE_APPLICATION).setWindowToken(
+ activity).build();
+ final WindowState startingWin = newWindowBuilder("startingWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
+ final WindowState baseWin = newWindowBuilder("baseWin",
+ TYPE_BASE_APPLICATION).setWindowToken(activity).build();
+ final WindowState win4 = newWindowBuilder("win4", TYPE_APPLICATION).setWindowToken(
+ activity).build();
// Should not contain the windows that were added above.
assertEquals(4, activity.getChildCount());
@@ -2492,14 +2571,17 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
assertNull(activity.findMainWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12");
+ final WindowState window1 = newWindowBuilder("window1",
+ TYPE_BASE_APPLICATION).setWindowToken(activity).build();
+ final WindowState window11 = newWindowBuilder("window11", FIRST_SUB_WINDOW).setParent(
+ window1).setWindowToken(activity).build();
+ final WindowState window12 = newWindowBuilder("window12", FIRST_SUB_WINDOW).setParent(
+ window1).setWindowToken(activity).build();
assertEquals(window1, activity.findMainWindow());
window1.mAnimatingExit = true;
assertEquals(window1, activity.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "window2");
+ final WindowState window2 = newWindowBuilder("window2",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
assertEquals(window2, activity.findMainWindow());
activity.removeImmediately();
}
@@ -2507,6 +2589,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testLandscapeSeascapeRotationByApp() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task task = new TaskBuilder(mSupervisor)
.setDisplay(mDisplayContent).setCreateActivity(true).build();
final ActivityRecord activity = task.getTopNonFinishingActivity();
@@ -2572,6 +2655,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
@Presubmit
public void testGetOrientation() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
// ActivityBuilder will resume top activities and cause the activity been added into
// opening apps list. Since this test is focus on the effect of visible on getting
// orientation, we skip app transition to avoid interference.
@@ -2642,8 +2726,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testStuckExitingWindow() {
- final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
- "closingWindow");
+ final WindowState closingWindow = newWindowBuilder("closingWindow",
+ FIRST_APPLICATION_WINDOW).build();
closingWindow.mAnimatingExit = true;
closingWindow.mRemoveOnExit = true;
closingWindow.mActivityRecord.commitVisibility(
@@ -2663,8 +2747,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testSetOrientation_restrictedByTargetSdk() {
mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
- mDisplayContent.setIgnoreOrientationRequest(true);
makeDisplayLargeScreen(mDisplayContent);
+ assertTrue(mDisplayContent.getIgnoreOrientationRequest());
assertSetOrientation(Build.VERSION_CODES.CUR_DEVELOPMENT, CATEGORY_SOCIAL, false);
assertSetOrientation(Build.VERSION_CODES.CUR_DEVELOPMENT, CATEGORY_GAME, true);
@@ -2702,6 +2786,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testRespectTopFullscreenOrientation() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Configuration displayConfig = activity.mDisplayContent.getConfiguration();
final Configuration activityConfig = activity.getConfiguration();
@@ -3243,7 +3328,6 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.isVisibleRequested());
player.start();
- mSetFlagsRule.enableFlags(Flags.FLAG_RESET_DRAW_STATE_ON_CLIENT_INVISIBLE);
// ActivityRecord#commitVisibility(false) -> WindowState#sendAppVisibilityToClients().
player.finish();
assertFalse(activity.isVisible());
@@ -3304,7 +3388,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
makeWindowVisibleAndDrawn(app, mImeWindow);
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
@@ -3332,7 +3416,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
public void testImeInsetsFrozenFlag_resetWhenReportedToBeImeInputTarget() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setWindowContainer(
mImeWindow, null, null);
@@ -3376,8 +3460,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testImeInsetsFrozenFlag_noDispatchVisibleInsetsWhenAppNotRequest()
throws RemoteException {
- final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
- final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+ final WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).build();
+ final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setWindowContainer(
mImeWindow, null, null);
@@ -3421,7 +3505,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testImeInsetsFrozenFlag_multiWindowActivities() {
final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).setWindowToken(
+ imeToken).build();
makeWindowVisibleAndDrawn(ime);
// Create a split-screen root task with activity1 and activity 2.
@@ -3442,8 +3527,10 @@ public class ActivityRecordTests extends WindowTestsBase {
activity1.mImeInsetsFrozenUntilStartInput = true;
activity2.mImeInsetsFrozenUntilStartInput = true;
- final WindowState app1 = createWindow(null, TYPE_APPLICATION, activity1, "app1");
- final WindowState app2 = createWindow(null, TYPE_APPLICATION, activity2, "app2");
+ final WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).setWindowToken(
+ activity1).build();
+ final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setWindowToken(
+ activity2).build();
makeWindowVisibleAndDrawn(app1, app2);
final InsetsStateController controller = mDisplayContent.getInsetsStateController();
@@ -3472,7 +3559,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testInClosingAnimation_visibilityNotCommitted_doNotHideSurface() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
makeWindowVisibleAndDrawn(app);
// Put the activity in close transition.
@@ -3499,7 +3586,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testInClosingAnimation_visibilityCommitted_hideSurface() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
makeWindowVisibleAndDrawn(app);
app.mActivityRecord.prepareSurfaces();
@@ -3686,6 +3773,37 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(appWindow.mResizeReported);
}
+ private ActivityRecord setupDisplayAndActivityForCameraCompat(boolean isCameraRunning,
+ int windowingMode) {
+ doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
+ // Create a new DisplayContent so that the flag values create the camera freeform policy.
+ mDisplayContent = new TestDisplayContent.Builder(mAtm, mDisplayContent.getSurfaceWidth(),
+ mDisplayContent.getSurfaceHeight()).build();
+ final CameraStateMonitor cameraStateMonitor = mDisplayContent.mAppCompatCameraPolicy
+ .mCameraStateMonitor;
+ spyOn(cameraStateMonitor);
+ doReturn(isCameraRunning).when(cameraStateMonitor).isCameraRunningForActivity(any());
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ spyOn(tda);
+ doReturn(true).when(tda).supportsNonResizableMultiWindow();
+ final Task rootTask = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent)
+ .setWindowingMode(windowingMode).build();
+ doReturn(mDisplayContent.getDisplayInfo())
+ .when(mDisplayContent.mWmService.mDisplayManagerInternal).getDisplayInfo(anyInt());
+ rootTask.setBounds(0, 0, 1000, 500);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setComponent(ComponentName.createRelative(mContext,
+ com.android.server.wm.ActivityRecordTests.class.getName()))
+ .setParentTask(rootTask)
+ .setCreateTask(true)
+ .setOnTop(true)
+ .setResizeMode(RESIZE_MODE_RESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+ activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ return activity;
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 2a53df9f8353..a7fc10f2fcc5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -17,30 +17,23 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import android.content.ComponentName;
-import android.graphics.ColorSpace;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
-import android.view.Surface;
import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
@@ -61,14 +54,20 @@ import java.util.Arrays;
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class ActivitySnapshotControllerTests extends WindowTestsBase {
+public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBase {
private ActivitySnapshotController mActivitySnapshotController;
+ public ActivitySnapshotControllerTests() {
+ super(0.8f /* highResScale */, 0.5f /* lowResScale */);
+ }
+
+ @Override
@Before
- public void setUp() throws Exception {
- spyOn(mWm.mSnapshotController.mActivitySnapshotController);
- mActivitySnapshotController = mWm.mSnapshotController.mActivitySnapshotController;
+ public void setUp() {
+ super.setUp();
+ mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
+ spyOn(mActivitySnapshotController);
doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
mActivitySnapshotController.resetTmpFields();
}
@@ -92,12 +91,11 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase {
assertEquals(0, mActivitySnapshotController.mPendingRemoveActivity.size());
mActivitySnapshotController.resetTmpFields();
- // simulate three activity
+ // simulate three activity, the bottom activity won't participate in transition
final WindowState belowClose = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
"belowClose");
belowClose.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- windows.add(belowClose.mActivityRecord);
mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size());
assertEquals(belowClose.mActivityRecord,
@@ -249,15 +247,28 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase {
assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities));
}
- private TaskSnapshot createSnapshot() {
- HardwareBuffer buffer = mock(HardwareBuffer.class);
- doReturn(100).when(buffer).getWidth();
- doReturn(100).when(buffer).getHeight();
- return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */,
- new Rect() /* letterboxInsets*/, false /* isLowResolution */,
- true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
- false /* isTranslucent */, false /* hasImeSurface */, 0 /* uiMode */);
+ /**
+ * Verifies that activity snapshot is skipped if the persister queue has too many pending write
+ * items.
+ */
+ @Test
+ public void testSkipRecordActivity() {
+ doReturn(createSnapshot()).when(mActivitySnapshotController).recordSnapshotInner(any());
+ final Task task = createTask(mDisplayContent);
+
+ mSnapshotPersistQueue.setPaused(true);
+ final ArrayList<ActivityRecord> tmpList = new ArrayList<>();
+ for (int i = 0; i < MAX_STORE_QUEUE_DEPTH; ++i) {
+ tmpList.clear();
+ final ActivityRecord activity = createActivityRecord(task);
+ tmpList.add(activity);
+ mActivitySnapshotController.recordSnapshot(tmpList);
+ assertNotNull(mActivitySnapshotController.findSavedFile(activity));
+ }
+ tmpList.clear();
+ final ActivityRecord activity = createActivityRecord(task);
+ tmpList.add(activity);
+ mActivitySnapshotController.recordSnapshot(tmpList);
+ assertNull(mActivitySnapshotController.findSavedFile(activity));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e0b29c937381..1cb1e3cae413 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -511,6 +511,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
@Test
public void testSupportsMultiWindow_nonResizable() {
final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setComponent(getUniqueComponentName(mContext.getPackageName()))
.setCreateTask(true)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 40da9ea2d718..579ed6659976 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -365,8 +365,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue(outPrevActivities.isEmpty());
assertTrue(predictable);
// reset
- tf1.setAdjacentTaskFragment(null);
- tf2.setAdjacentTaskFragment(null);
+ tf1.clearAdjacentTaskFragments();
+ tf2.clearAdjacentTaskFragments();
tf1.setCompanionTaskFragment(null);
tf2.setCompanionTaskFragment(null);
@@ -398,8 +398,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue(predictable);
// reset
outPrevActivities.clear();
- tf2.setAdjacentTaskFragment(null);
- tf3.setAdjacentTaskFragment(null);
+ tf2.clearAdjacentTaskFragments();
+ tf3.clearAdjacentTaskFragments();
final TaskFragment tf4 = createTaskFragmentWithActivity(task);
// Stacked + next companion to top => predict for previous activity below companion.
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 2ceff62203d6..748a47a520d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -31,6 +31,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -46,17 +49,20 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.CameraCompatTaskInfo;
+import android.app.IApplicationThread;
import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.RefreshCallbackItem;
import android.app.servertransaction.ResumeActivityItem;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Configuration.Orientation;
import android.graphics.Rect;
@@ -77,6 +83,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.concurrent.Executor;
@@ -138,7 +145,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(mDisplayContent,
cameraStateMonitor, mActivityRefresher);
- setDisplayRotation(Surface.ROTATION_90);
+ setDisplayRotation(ROTATION_90);
mCameraCompatFreeformPolicy.start();
cameraStateMonitor.startListeningToCameraState();
}
@@ -196,6 +203,58 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
}
@Test
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testIsFreeformLetterboxingForCameraAllowed_featureDisabled_returnsFalse() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testIsFreeformLetterboxingForCameraAllowed_overrideDisabled_returnsFalse() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testIsFreeformLetterboxingForCameraAllowed_cameraNotRunning_returnsFalse() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testIsFreeformLetterboxingForCameraAllowed_notFreeformWindowing_returnsFalse() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testIsFreeformLetterboxingForCameraAllowed_optInFreeformCameraRunning_true() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertTrue(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testFullscreen_doesNotActivateCameraCompatMode() {
@@ -229,7 +288,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_0);
+ setDisplayRotation(ROTATION_0);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT);
@@ -241,7 +300,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
@@ -253,7 +312,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
- setDisplayRotation(Surface.ROTATION_0);
+ setDisplayRotation(ROTATION_0);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT);
@@ -265,7 +324,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE);
@@ -277,7 +336,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
callOnActivityConfigurationChanging(mActivity, /* letterboxNew= */ true,
@@ -467,6 +526,36 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
/* delta= */ 0.001);
}
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOnCameraOpened_portraitActivity_sandboxesDisplayRotationAndUpdatesApp() throws
+ Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ setDisplayRotation(ROTATION_270);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ // This is a portrait rotation for a device with portrait natural orientation (most common,
+ // currently the only one supported).
+ assertCompatibilityInfoSentWithDisplayRotation(ROTATION_0);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOnCameraOpened_landscapeActivity_sandboxesDisplayRotationAndUpdatesApp() throws
+ Exception {
+ configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
+ setDisplayRotation(ROTATION_0);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ // This is a landscape rotation for a device with portrait natural orientation (most common,
+ // currently the only one supported).
+ assertCompatibilityInfoSentWithDisplayRotation(ROTATION_90);
+ }
+
private void configureActivity(@ScreenOrientation int activityOrientation) {
configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
}
@@ -499,6 +588,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
doReturn(windowingMode == WINDOWING_MODE_FREEFORM).when(mActivity)
.inFreeformWindowingMode();
+ setupMockApplicationThread();
}
private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) {
@@ -557,9 +647,27 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
// case for most standard phones and tablets.
// TODO(b/365725400): handle landscape natural orientation.
displayInfo.logicalHeight = displayRotation % 180 == 0 ? 800 : 600;
- displayInfo.logicalWidth = displayRotation % 180 == 0 ? 600 : 800;
+ displayInfo.logicalWidth = displayRotation % 180 == 0 ? 600 : 800;
return displayInfo;
}).when(mDisplayContent.mWmService.mDisplayManagerInternal)
.getDisplayInfo(anyInt());
}
+
+ private void setupMockApplicationThread() {
+ IApplicationThread mockApplicationThread = mock(IApplicationThread.class);
+ spyOn(mActivity.app);
+ doReturn(mockApplicationThread).when(mActivity.app).getThread();
+ }
+
+ private void assertCompatibilityInfoSentWithDisplayRotation(@Surface.Rotation int
+ expectedRotation) throws Exception {
+ final ArgumentCaptor<CompatibilityInfo> compatibilityInfoArgumentCaptor =
+ ArgumentCaptor.forClass(CompatibilityInfo.class);
+ verify(mActivity.app.getThread()).updatePackageCompatibilityInfo(eq(mActivity.packageName),
+ compatibilityInfoArgumentCaptor.capture());
+
+ final CompatibilityInfo compatInfo = compatibilityInfoArgumentCaptor.getValue();
+ assertTrue(compatInfo.isOverrideDisplayRotationRequired());
+ assertEquals(expectedRotation, compatInfo.applicationDisplayRotation);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index 87dbca51e24e..060b379c1281 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -84,6 +84,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase {
@Test
public void testGetRequestedOrientationForDisplay() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build();
final ActivityRecord activity = task.getTopNonFinishingActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 7b2cd63b4afb..0a7df5a305bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -491,6 +491,7 @@ public class DisplayAreaTest extends WindowTestsBase {
@Test
public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor() {
final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task stack =
new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
final ActivityRecord activity = stack.getTopNonFinishingActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 9cbea2e2f0ad..db71f2bf039d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -898,6 +898,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testOrientationDefinedByKeyguard() {
final DisplayContent dc = mDisplayContent;
dc.getDisplayPolicy().setAwake(true);
+ dc.setIgnoreOrientationRequest(false);
// Create a window that requests landscape orientation. It will define device orientation
// by default.
@@ -925,6 +926,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testOrientationForAspectRatio() {
final DisplayContent dc = createNewDisplay();
+ dc.setIgnoreOrientationRequest(false);
// When display content is created its configuration is not yet initialized, which could
// cause unnecessary configuration propagation, so initialize it here.
@@ -1034,6 +1036,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testAllowsTopmostFullscreenOrientation() {
final DisplayContent dc = createNewDisplay();
+ dc.setIgnoreOrientationRequest(false);
assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, dc.getOrientation());
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
@@ -1112,6 +1115,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testOnDescendantOrientationRequestChanged() {
final DisplayContent dc = createNewDisplay();
+ dc.setIgnoreOrientationRequest(false);
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -1130,6 +1134,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
final DisplayContent dc = createNewDisplay();
+ dc.setIgnoreOrientationRequest(false);
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
@@ -1152,6 +1157,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testOrientationBehind() {
assertNull(mDisplayContent.getLastOrientationSource());
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true)
.setScreenOrientation(getRotatedOrientation(mDisplayContent)).build();
prev.setVisibleRequested(false);
@@ -1172,6 +1178,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testFixedToUserRotationChanged() {
final DisplayContent dc = createNewDisplay();
+ dc.setIgnoreOrientationRequest(false);
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
@@ -1589,6 +1596,7 @@ public class DisplayContentTests extends WindowTestsBase {
W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
public void testApplyTopFixedRotationTransform() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
spyOn(displayPolicy);
// Only non-movable (gesture) navigation bar will be animated by fixed rotation animation.
@@ -1742,6 +1750,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
+ displayContent.setIgnoreOrientationRequest(false);
unblockDisplayRotation(displayContent);
// Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded.
doNothing().when(displayContent).prepareAppTransition(anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
index 63973345b5fb..6527af1ec704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
@@ -77,7 +77,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas
when(mMockActivityRecord.findMainWindow()).thenReturn(mMockWindowState);
doReturn(mMockActivityRecord).when(mDisplayContent).topRunningActivity();
- when(mDisplayContent.getIgnoreOrientationRequest()).thenReturn(true);
+ mDisplayContent.setIgnoreOrientationRequest(true);
mMockAppCompatConfiguration = mock(AppCompatConfiguration.class);
when(mMockAppCompatConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled())
@@ -195,7 +195,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas
@Test
public void testIsRotationLockEnforced_ignoreOrientationRequestDisabled_lockNotEnforced() {
- when(mDisplayContent.getIgnoreOrientationRequest()).thenReturn(false);
+ mDisplayContent.setIgnoreOrientationRequest(false);
assertIsRotationLockEnforcedReturnsFalseForAllRotations();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index f339d292ed82..429a396ad997 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -92,7 +92,7 @@ import java.util.concurrent.TimeUnit;
* Tests for the {@link DragDropController} class.
*
* Build/Install/Run:
- * atest WmTests:DragDropControllerTests
+ * atest WmTests:DragDropControllerTests
*/
@SmallTest
@Presubmit
@@ -146,12 +146,12 @@ public class DragDropControllerTests extends WindowTestsBase {
*/
private WindowState createDropTargetWindow(String name, int ownerId) {
final Task task = new TaskBuilder(mSupervisor).setUserId(ownerId).build();
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
- .setUseProcess(mProcess).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).setUseProcess(
+ mProcess).build();
// Use a new TestIWindow so we don't collect events for other windows
- final WindowState window = createWindow(
- null, TYPE_BASE_APPLICATION, activity, name, ownerId, false, new TestIWindow());
+ final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, activity, name,
+ ownerId, false, new TestIWindow());
InputChannel channel = new InputChannel();
window.openInputChannel(channel);
window.mHasSurface = true;
@@ -173,12 +173,11 @@ public class DragDropControllerTests extends WindowTestsBase {
@Before
public void setUp() throws Exception {
mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
- mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc",
- TEST_PID, TEST_UID);
+ mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc", TEST_PID, TEST_UID);
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
- when(mWm.mInputManager.startDragAndDrop(any(IBinder.class),
- any(IBinder.class))).thenReturn(true);
+ when(mWm.mInputManager.startDragAndDrop(any(IBinder.class), any(IBinder.class))).thenReturn(
+ true);
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@@ -286,16 +285,15 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify the start-drag event is sent for the local and global intercept window
// but not the other window
assertTrue(nonLocalWindowDragEvents.isEmpty());
- assertTrue(localWindowDragEvents.get(0).getAction()
- == ACTION_DRAG_STARTED);
+ assertTrue(localWindowDragEvents.get(0).getAction() == ACTION_DRAG_STARTED);
assertTrue(globalInterceptWindowDragEvents.get(0).getAction()
== ACTION_DRAG_STARTED);
// Verify that only the global intercept window receives the clip data with the
// resolved activity info for the drag
assertNull(localWindowDragEvents.get(0).getClipData());
- assertTrue(globalInterceptWindowDragEvents.get(0).getClipData()
- .willParcelWithActivityInfo());
+ assertTrue(globalInterceptWindowDragEvents.get(
+ 0).getClipData().willParcelWithActivityInfo());
mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0);
mTarget.handleMotionEvent(false, 0, 0);
@@ -330,9 +328,8 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify the start-drag event has the drag flags
final DragEvent dragEvent = dragEvents.get(0);
assertTrue(dragEvent.getAction() == ACTION_DRAG_STARTED);
- assertTrue(dragEvent.getDragFlags() ==
- (View.DRAG_FLAG_GLOBAL
- | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
+ assertTrue(dragEvent.getDragFlags() == (View.DRAG_FLAG_GLOBAL
+ | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
try {
mTarget.mDeferDragStateClosed = true;
@@ -340,9 +337,8 @@ public class DragDropControllerTests extends WindowTestsBase {
// // Verify the drop event does not have the drag flags
mTarget.handleMotionEvent(false, 0, 0);
final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
- assertTrue(dropEvent.getDragFlags() ==
- (View.DRAG_FLAG_GLOBAL
- | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
+ assertTrue(dropEvent.getDragFlags() == (View.DRAG_FLAG_GLOBAL
+ | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
mTarget.reportDropResult(iwindow, true);
} finally {
@@ -385,16 +381,15 @@ public class DragDropControllerTests extends WindowTestsBase {
data.putExtra(Intent.EXTRA_USER, user);
}
final ClipData clipData = new ClipData(
- new ClipDescription("drag", new String[] {
- MIMETYPE_APPLICATION_ACTIVITY}),
+ new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_ACTIVITY}),
new ClipData.Item(data));
return clipData;
}
@Test
public void testValidateAppShortcutArguments() {
- doReturn(PERMISSION_GRANTED).when(mWm.mContext)
- .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+ eq(START_TASKS_FROM_RECENTS));
final Session session = createTestSession(mAtm);
try {
session.validateAndResolveDragMimeTypeExtras(
@@ -414,8 +409,8 @@ public class DragDropControllerTests extends WindowTestsBase {
}
try {
session.validateAndResolveDragMimeTypeExtras(
- createClipDataForShortcut("test_package", "test_shortcut_id", null),
- TEST_UID, TEST_PID, TEST_PACKAGE);
+ createClipDataForShortcut("test_package", "test_shortcut_id", null), TEST_UID,
+ TEST_PID, TEST_PACKAGE);
fail("Expected failure without package name");
} catch (IllegalArgumentException e) {
// Expected failure
@@ -424,8 +419,8 @@ public class DragDropControllerTests extends WindowTestsBase {
@Test
public void testValidateProfileAppShortcutArguments_notCallingUid() {
- doReturn(PERMISSION_GRANTED).when(mWm.mContext)
- .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+ eq(START_TASKS_FROM_RECENTS));
final Session session = createTestSession(mAtm);
final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class);
final Intent[] shortcutIntents = new Intent[1];
@@ -438,10 +433,9 @@ public class DragDropControllerTests extends WindowTestsBase {
ArgumentCaptor<Integer> callingUser = ArgumentCaptor.forClass(Integer.class);
session.validateAndResolveDragMimeTypeExtras(
createClipDataForShortcut("test_package", "test_shortcut_id",
- mock(UserHandle.class)),
- TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
- verify(shortcutService).createShortcutIntents(callingUser.capture(), any(),
- any(), any(), anyInt(), anyInt(), anyInt());
+ mock(UserHandle.class)), TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
+ verify(shortcutService).createShortcutIntents(callingUser.capture(), any(), any(), any(),
+ anyInt(), anyInt(), anyInt());
assertTrue(callingUser.getValue() == UserHandle.getUserId(TEST_PROFILE_UID));
}
@@ -458,20 +452,19 @@ public class DragDropControllerTests extends WindowTestsBase {
data.putExtra(Intent.EXTRA_USER, user);
}
final ClipData clipData = new ClipData(
- new ClipDescription("drag", new String[] {
- MIMETYPE_APPLICATION_SHORTCUT}),
+ new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_SHORTCUT}),
new ClipData.Item(data));
return clipData;
}
@Test
public void testValidateAppTaskArguments() {
- doReturn(PERMISSION_GRANTED).when(mWm.mContext)
- .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+ eq(START_TASKS_FROM_RECENTS));
final Session session = createTestSession(mAtm);
try {
final ClipData clipData = new ClipData(
- new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }),
+ new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_TASK}),
new ClipData.Item(new Intent()));
session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID, TEST_PID,
@@ -496,8 +489,8 @@ public class DragDropControllerTests extends WindowTestsBase {
@Test
public void testValidateFlagsWithPermission() {
- doReturn(PERMISSION_GRANTED).when(mWm.mContext)
- .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+ eq(START_TASKS_FROM_RECENTS));
final Session session = createTestSession(mAtm);
try {
session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
@@ -533,8 +526,8 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify the DRAG_ENDED event does NOT include the drag surface
final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
- assertTrue(dragEvents.get(dragEvents.size() - 1).getAction()
- == ACTION_DRAG_ENDED);
+ assertTrue(
+ dragEvents.get(dragEvents.size() - 1).getAction() == ACTION_DRAG_ENDED);
assertTrue(dropEvent.getDragSurface() == null);
});
}
@@ -564,8 +557,8 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify the DRAG_ENDED event includes the drag surface
final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
- assertTrue(dragEvents.get(dragEvents.size() - 1).getAction()
- == ACTION_DRAG_ENDED);
+ assertTrue(
+ dragEvents.get(dragEvents.size() - 1).getAction() == ACTION_DRAG_ENDED);
assertTrue(dropEvent.getDragSurface() != null);
});
}
@@ -591,18 +584,18 @@ public class DragDropControllerTests extends WindowTestsBase {
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
ClipData.newPlainText("label", "Test"), () -> {
- // Trigger an unhandled drop and verify the global drag listener was called
- mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
- mTarget.reportDropResult(mWindow.mClient, false);
- mTarget.onUnhandledDropCallback(true);
- mToken = null;
- try {
- verify(listener, times(1)).onUnhandledDrop(any(), any());
- } catch (RemoteException e) {
- fail("Failed to verify unhandled drop: " + e);
- }
- });
+ // Trigger an unhandled drop and verify the global drag listener was called
+ mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.reportDropResult(mWindow.mClient, false);
+ mTarget.onUnhandledDropCallback(true);
+ mToken = null;
+ try {
+ verify(listener, times(1)).onUnhandledDrop(any(), any());
+ } catch (RemoteException e) {
+ fail("Failed to verify unhandled drop: " + e);
+ }
+ });
}
@Test
@@ -615,17 +608,17 @@ public class DragDropControllerTests extends WindowTestsBase {
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
ClipData.newPlainText("label", "Test"), () -> {
- // Trigger an unhandled drop and verify the global drag listener was called
- mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
- mTarget.onUnhandledDropCallback(true);
- mToken = null;
- try {
- verify(listener, times(1)).onUnhandledDrop(any(), any());
- } catch (RemoteException e) {
- fail("Failed to verify unhandled drop: " + e);
- }
- });
+ // Trigger an unhandled drop and verify the global drag listener was called
+ mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.onUnhandledDropCallback(true);
+ mToken = null;
+ try {
+ verify(listener, times(1)).onUnhandledDrop(any(), any());
+ } catch (RemoteException e) {
+ fail("Failed to verify unhandled drop: " + e);
+ }
+ });
}
@Test
@@ -636,18 +629,17 @@ public class DragDropControllerTests extends WindowTestsBase {
doReturn(mock(Binder.class)).when(listener).asBinder();
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
- startDrag(View.DRAG_FLAG_GLOBAL,
- ClipData.newPlainText("label", "Test"), () -> {
- // Trigger an unhandled drop and verify the global drag listener was not called
- mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
- mToken = null;
- try {
- verify(listener, never()).onUnhandledDrop(any(), any());
- } catch (RemoteException e) {
- fail("Failed to verify unhandled drop: " + e);
- }
- });
+ startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
+ // Trigger an unhandled drop and verify the global drag listener was not called
+ mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mToken = null;
+ try {
+ verify(listener, never()).onUnhandledDrop(any(), any());
+ } catch (RemoteException e) {
+ fail("Failed to verify unhandled drop: " + e);
+ }
+ });
}
@Test
@@ -660,20 +652,22 @@ public class DragDropControllerTests extends WindowTestsBase {
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
ClipData.newPlainText("label", "Test"), () -> {
- // Trigger an unhandled drop and verify the global drag listener was called
- mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ // Trigger an unhandled drop and verify the global drag listener was called
+ mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
- // Verify that the unhandled drop listener callback timeout has been scheduled
- final Handler handler = mTarget.getHandler();
- assertTrue(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+ // Verify that the unhandled drop listener callback timeout has been scheduled
+ final Handler handler = mTarget.getHandler();
+ assertTrue(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
- // Force trigger the timeout and verify that it actually cleans up the drag & timeout
- handler.handleMessage(Message.obtain(handler, MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
- assertFalse(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
- assertFalse(mTarget.dragDropActiveLocked());
- mToken = null;
- });
+ // Force trigger the timeout and verify that it actually cleans up the drag &
+ // timeout
+ handler.handleMessage(
+ Message.obtain(handler, MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+ assertFalse(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+ assertFalse(mTarget.dragDropActiveLocked());
+ mToken = null;
+ });
}
private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
@@ -690,15 +684,13 @@ public class DragDropControllerTests extends WindowTestsBase {
private void startDrag(int flag, ClipData data, Runnable r) {
final SurfaceSession appSession = new SurfaceSession();
try {
- final SurfaceControl surface = new SurfaceControl.Builder(appSession)
- .setName("drag surface")
- .setBufferSize(100, 100)
- .setFormat(PixelFormat.TRANSLUCENT)
- .build();
+ final SurfaceControl surface = new SurfaceControl.Builder(appSession).setName(
+ "drag surface").setBufferSize(100, 100).setFormat(
+ PixelFormat.TRANSLUCENT).build();
assertTrue(mWm.mInputManager.startDragAndDrop(new Binder(), new Binder()));
- mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
- flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
+ mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
+ 0, 0, data);
assertNotNull(mToken);
r.run();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index 708d6860abc2..bd15bc42e811 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -106,6 +106,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
// Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait).
mDisplay = new DualDisplayContent.Builder(mAtm, 1920, 1200).build();
+ // The test verifies that the display area can affect display's getLastOrientation().
+ mDisplay.setIgnoreOrientationRequest(false);
mFirstRoot = mDisplay.mFirstRoot;
mSecondRoot = mDisplay.mSecondRoot;
mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index aa992504f9a5..25b9f4b8035b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -717,13 +717,13 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents() {
testVisibleTasks_excludedFromRecents_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_internal();
}
@@ -767,13 +767,13 @@ public class RecentTasksTest extends WindowTestsBase {
@Test
@Ignore("b/342627272")
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask() {
testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
}
@@ -816,13 +816,13 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 7cb62c5a6769..d96512588c7c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -132,6 +132,7 @@ public class RootTaskTests extends WindowTestsBase {
@Test
public void testClosingAppDifferentTaskOrientation() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -146,6 +147,7 @@ public class RootTaskTests extends WindowTestsBase {
@Test
public void testMoveTaskToBackDifferentTaskOrientation() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 7e8bd38fb6a9..699ed0263756 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -41,6 +41,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -63,6 +64,7 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
@@ -77,12 +79,14 @@ import android.graphics.Rect;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import androidx.test.filters.MediumTest;
import com.android.internal.app.ResolverActivity;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -693,6 +697,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testAwakeFromSleepingWithAppConfiguration() {
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ display.setIgnoreOrientationRequest(false);
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity.moveFocusableActivityToTop("test");
assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay());
@@ -1331,6 +1336,38 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
}
+ @EnableFlags(Flags.FLAG_ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING)
+ @Test
+ public void testSwitchUser_withVisibleRootTasks_storesAllVisibleRootTasksForCurrentUser() {
+ // Set up root tasks
+ final Task rootTask1 = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task rootTask2 = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task rootTask3 = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ doReturn(rootTask3).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+ // Set up user ids and visibility
+ rootTask1.mUserId = mRootWindowContainer.mCurrentUser;
+ rootTask2.mUserId = mRootWindowContainer.mCurrentUser;
+ rootTask3.mUserId = mRootWindowContainer.mCurrentUser;
+ rootTask1.mVisibleRequested = false;
+ rootTask2.mVisibleRequested = true;
+ rootTask3.mVisibleRequested = true;
+
+ // Switch to a different user
+ int currentUser = mRootWindowContainer.mCurrentUser;
+ int otherUser = currentUser + 1;
+ mRootWindowContainer.switchUser(otherUser, null);
+
+ // Verify that the previous user persists it's previous visible root tasks
+ assertArrayEquals(
+ new int[]{rootTask2.mTaskId, rootTask3.mTaskId},
+ mRootWindowContainer.mUserVisibleRootTasks.get(currentUser).toArray()
+ );
+ }
+
@Test
public void testLockAllProfileTasks() {
final int profileUid = UserHandle.PER_USER_RANGE + UserHandle.MIN_SECONDARY_USER_ID;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index bf96f0eb03b8..201ff51f1495 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -107,6 +107,8 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -210,6 +212,37 @@ public class SizeCompatTests extends WindowTestsBase {
return setUpApp(builder.build(), appBuilder);
}
+ private void setUpLargeScreenDisplayWithApp(int dw, int dh) {
+ final DisplayContent display = mDisplayContent;
+ final DisplayInfo displayInfo = display.getDisplayInfo();
+ displayInfo.logicalWidth = dw;
+ displayInfo.logicalHeight = dh;
+ // Prevent legacy sdk from being affected by INSETS_DECOUPLED_CONFIGURATION_ENFORCED.
+ display.mInitialDisplayCutout = displayInfo.displayCutout = DisplayCutout.NO_CUTOUT;
+ // Smallest screen width=747dp according to 1400/(300/160).
+ display.mBaseDisplayDensity = displayInfo.logicalDensityDpi =
+ TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY;
+ doNothing().when(display).updateDisplayInfo(any());
+ resizeDisplay(display, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ assertTrue(display.isLargeScreen());
+ if (com.android.window.flags.Flags.universalResizableByDefault()) {
+ assertTrue("Large screen must ignore orientation request",
+ display.getIgnoreOrientationRequest());
+ } else {
+ display.setIgnoreOrientationRequest(true);
+ }
+ setUpApp(display, null /* appBuilder */);
+ spyOn(display.getDisplayRotation());
+ }
+
+ private void setUpLandscapeLargeScreenDisplayWithApp() {
+ setUpLargeScreenDisplayWithApp(/* dw */ 2800, /* dh */ 1400);
+ }
+
+ private void setUpPortraitLargeScreenDisplayWithApp() {
+ setUpLargeScreenDisplayWithApp(/* dw */ 1400, /* dh */ 2800);
+ }
+
@Test
public void testHorizontalReachabilityEnabledForTranslucentActivities() {
testReachabilityEnabledForTranslucentActivity(/* dw */ 2500, /* dh */1000,
@@ -658,9 +691,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testIsLetterboxed_activityFromBubble_returnsFalse() {
- setUpDisplaySizeWithApp(1000, 2500);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- spyOn(mActivity);
+ setUpPortraitLargeScreenDisplayWithApp();
doReturn(true).when(mActivity).getLaunchedFromBubble();
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
@@ -1694,10 +1725,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testGetLetterboxInnerBounds_noScalingApplied() {
// Set up a display in portrait and ignoring orientation request.
- final int dw = 1400;
- final int dh = 2800;
- setUpDisplaySizeWithApp(dw, dh);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpPortraitLargeScreenDisplayWithApp();
+ final int dw = mDisplayContent.mBaseDisplayWidth;
+ final int dh = mDisplayContent.mBaseDisplayHeight;
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
@@ -1823,8 +1853,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedLetterbox() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -1852,8 +1881,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
// orientation letterbox.
@@ -1884,8 +1912,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
// orientation letterbox.
@@ -1915,8 +1942,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
final float fixedOrientationLetterboxAspectRatio = 1.1f;
mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(
@@ -2011,8 +2037,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
final float fixedOrientationLetterboxAspectRatio = 1.1f;
mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(
@@ -2045,13 +2070,10 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testComputeConfigResourceOverrides_unresizableApp() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
- final Rect activityBounds = new Rect(mActivity.getBounds());
-
int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp;
int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp;
@@ -2068,7 +2090,7 @@ public class SizeCompatTests extends WindowTestsBase {
// After we rotate, the activity should go in the size-compat mode and report the same
// configuration values.
- assertDownScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
assertEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp);
assertEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp);
@@ -2087,14 +2109,11 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testComputeConfigResourceOverrides_resizableFixedOrientationActivity() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app without max aspect.
prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
- final Rect activityBounds = new Rect(mActivity.getBounds());
-
int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp;
int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp;
@@ -2206,10 +2225,10 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testSystemFullscreenOverrideForLandscapeDisplay() {
- final int displayWidth = 1600;
- final int displayHeight = 1400;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
+
spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
@@ -2226,10 +2245,10 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testSystemFullscreenOverrideForPortraitDisplay() {
- final int displayWidth = 1400;
- final int displayHeight = 1600;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpPortraitLargeScreenDisplayWithApp();
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
+
spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
@@ -2619,7 +2638,7 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
public void testOverrideRespectRequestedOrientationIsEnabled_orientationIsRespected() {
// Set up a display in landscape
- setUpDisplaySizeWithApp(2800, 1400);
+ setUpDisplaySizeWithApp(1000, 500);
final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
@@ -2636,7 +2655,7 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
public void testOverrideRespectRequestedOrientationIsEnabled_multiWindow_orientationIgnored() {
// Set up a display in landscape
- setUpDisplaySizeWithApp(2800, 1400);
+ setUpDisplaySizeWithApp(1000, 500);
final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
@@ -2655,10 +2674,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testSplitAspectRatioForUnresizableLandscapeApps() {
// Set up a display in portrait and ignoring orientation request.
- int screenWidth = 1400;
- int screenHeight = 1600;
- setUpDisplaySizeWithApp(screenWidth, screenHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLargeScreenDisplayWithApp(1400, 2400);
+ final int screenWidth = mDisplayContent.mBaseDisplayWidth;
+ final int screenHeight = mDisplayContent.mBaseDisplayHeight;
mActivity.mWmService.mAppCompatConfiguration
.setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true);
@@ -2692,10 +2710,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayAspectRatioForResizablePortraitApps() {
// Set up a display in portrait and ignoring orientation request.
- int displayWidth = 1400;
- int displayHeight = 1600;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLargeScreenDisplayWithApp(1400, 2400);
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
// Enable display aspect ratio to take precedence before
@@ -2728,10 +2745,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayAspectRatioForResizableLandscapeApps() {
// Set up a display in landscape and ignoring orientation request.
- int displayWidth = 1600;
- int displayHeight = 1400;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
// Enable display aspect ratio to take precedence before
@@ -2764,10 +2780,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayAspectRatioForUnresizableLandscapeApps() {
// Set up a display in portrait and ignoring orientation request.
- int displayWidth = 1400;
- int displayHeight = 1600;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpPortraitLargeScreenDisplayWithApp();
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
// Enable display aspect ratio to take precedence before
@@ -2791,10 +2806,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayAspectRatioForUnresizablePortraitApps() {
// Set up a display in landscape and ignoring orientation request.
- int displayWidth = 1600;
- int displayHeight = 1400;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
+ final int displayWidth = mDisplayContent.mBaseDisplayWidth;
+ final int displayHeight = mDisplayContent.mBaseDisplayHeight;
mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
// Enable display aspect ratio to take precedence before
@@ -2819,8 +2833,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -2837,7 +2850,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
- assertDownScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
assertActivityMaxBoundsSandboxed();
@@ -2846,8 +2859,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_sizeCompatAfterRotate() {
// Set up a display in portrait and ignoring orientation request.
- setUpDisplaySizeWithApp(1400, 2800);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpPortraitLargeScreenDisplayWithApp();
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -2882,9 +2894,8 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInLetterbox() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
+ setUpLandscapeLargeScreenDisplayWithApp();
final DisplayContent display = mActivity.mDisplayContent;
- display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -2928,9 +2939,7 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- final DisplayContent display = mActivity.mDisplayContent;
- display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -2950,9 +2959,8 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_newLaunchedMaxAspectApp() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
+ setUpLandscapeLargeScreenDisplayWithApp();
final DisplayContent display = mActivity.mDisplayContent;
- display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -3001,9 +3009,7 @@ public class SizeCompatTests extends WindowTestsBase {
@SuppressWarnings("GuardedBy")
public void testDisplayIgnoreOrientationRequest_pausedAppNotLostSizeCompat() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- final DisplayContent display = mActivity.mDisplayContent;
- display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
// Portrait fixed app.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -3019,7 +3025,6 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
- assertDownScaled();
assertThat(mActivity.inSizeCompatMode()).isTrue();
// Activity max bounds are sandboxed due to size compat mode.
assertActivityMaxBoundsSandboxed();
@@ -3035,7 +3040,7 @@ public class SizeCompatTests extends WindowTestsBase {
verify(scmPolicy, never()).clearSizeCompatMode();
assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
- assertDownScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
assertEquals(activityBounds, mActivity.getBounds());
// Activity max bounds are sandboxed due to size compat.
assertActivityMaxBoundsSandboxed();
@@ -3044,9 +3049,8 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testDisplayIgnoreOrientationRequest_rotated180_notInSizeCompat() {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
+ setUpLandscapeLargeScreenDisplayWithApp();
final DisplayContent display = mActivity.mDisplayContent;
- display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -3063,7 +3067,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
- assertDownScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
assertActivityMaxBoundsSandboxed();
// Rotate display to landscape.
@@ -3246,8 +3250,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testTaskDisplayAreaNotFillDisplay() {
- setUpDisplaySizeWithApp(1400, 2800);
+ setUpPortraitLargeScreenDisplayWithApp();
final DisplayContent display = mActivity.mDisplayContent;
+ display.setIgnoreOrientationRequest(false);
final TaskDisplayArea taskDisplayArea = mActivity.getDisplayArea();
taskDisplayArea.setBounds(0, 0, 1000, 2400);
@@ -3430,8 +3435,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testIsHorizontalReachabilityEnabled_splitScreen_false() {
mAtm.mDevEnableNonResizableMultiWindow = true;
- setUpDisplaySizeWithApp(2800, 1000);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mWm.mAppCompatConfiguration.setIsHorizontalReachabilityEnabled(true);
setUpAllowThinLetterboxed(/* thinLetterboxAllowed */ true);
final TestSplitOrganizer organizer =
@@ -3518,8 +3522,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testIsHorizontalReachabilityEnabled_emptyBounds_true() {
- setUpDisplaySizeWithApp(/* dw */ 2800, /* dh */ 1000);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mWm.mAppCompatConfiguration.setIsHorizontalReachabilityEnabled(true);
setUpAllowThinLetterboxed(/* thinLetterboxAllowed */ true);
@@ -3566,8 +3569,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
- setUpDisplaySizeWithApp(2800, 1000);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mWm.mAppCompatConfiguration.setIsHorizontalReachabilityEnabled(true);
setUpAllowThinLetterboxed(/* thinLetterboxAllowed */ true);
@@ -3787,12 +3789,10 @@ public class SizeCompatTests extends WindowTestsBase {
}
private void assertLandscapeActivityAlignedToBottomWithNavbar(boolean immersive) {
- final int screenHeight = 2800;
- final int screenWidth = 1400;
+ setUpPortraitLargeScreenDisplayWithApp();
+ final int screenHeight = mDisplayContent.mBaseDisplayHeight;
+ final int screenWidth = mDisplayContent.mBaseDisplayWidth;
final int taskbarHeight = 200;
- setUpDisplaySizeWithApp(screenWidth, screenHeight);
-
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true);
mActivity.mWmService.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(1.0f);
final InsetsSource navSource = new InsetsSource(
@@ -3972,8 +3972,7 @@ public class SizeCompatTests extends WindowTestsBase {
float letterboxHorizontalPositionMultiplier, Rect fixedOrientationLetterbox,
Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
// Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mActivity.mWmService.mAppCompatConfiguration.setLetterboxHorizontalPositionMultiplier(
letterboxHorizontalPositionMultiplier);
@@ -4177,13 +4176,28 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpLandscapeLargeScreenDisplayWithApp();
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Consumer<Float> assertHorizontalPosition = letterboxHorizontalPositionMultiplier -> {
+ mActivity.mWmService.mAppCompatConfiguration.setLetterboxHorizontalPositionMultiplier(
+ letterboxHorizontalPositionMultiplier);
+ mActivity.recomputeConfiguration();
+ assertFitted();
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ assertTrue(mActivity.inSizeCompatMode());
+ // Activity is in size compat mode but not scaled.
+ assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
+ if (letterboxHorizontalPositionMultiplier < 1f) {
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
+ }
+ };
// When activity width equals parent width, multiplier shouldn't have any effect.
- assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
- /* letterboxHorizontalPositionMultiplier */ 0.0f);
- assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
- /* letterboxHorizontalPositionMultiplier */ 0.5f);
- assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
- /* letterboxHorizontalPositionMultiplier */ 1.0f);
+ assertHorizontalPosition.accept(0.0f);
+ assertHorizontalPosition.accept(0.5f);
+ assertHorizontalPosition.accept(1.0f);
}
@Test
@@ -4354,8 +4368,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testUpdateResolvedBoundsHorizontalPosition_bookModeEnabled() {
// Set up a display in landscape with a fixed-orientation PORTRAIT app
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mWm.mAppCompatConfiguration.setIsAutomaticReachabilityInBookModeEnabled(true);
mWm.mAppCompatConfiguration.setLetterboxHorizontalPositionMultiplier(
1.0f /*letterboxHorizontalPositionMultiplier*/);
@@ -4380,8 +4393,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testUpdateResolvedBoundsHorizontalPosition_bookModeDisabled_centered() {
// Set up a display in landscape with a fixed-orientation PORTRAIT app
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpLandscapeLargeScreenDisplayWithApp();
mWm.mAppCompatConfiguration.setIsAutomaticReachabilityInBookModeEnabled(false);
mWm.mAppCompatConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
prepareUnresizable(mActivity, 1.75f, SCREEN_ORIENTATION_PORTRAIT);
@@ -4462,8 +4474,7 @@ public class SizeCompatTests extends WindowTestsBase {
float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
// Set up a display in portrait and ignoring orientation request.
- setUpDisplaySizeWithApp(1400, 2800);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ setUpPortraitLargeScreenDisplayWithApp();
mActivity.mWmService.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(
letterboxVerticalPositionMultiplier);
@@ -5036,23 +5047,6 @@ public class SizeCompatTests extends WindowTestsBase {
return (dimensionToSplit - (dividerWindowWidth - dividerInsets * 2)) / 2;
}
- private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
- float letterboxHorizontalPositionMultiplier) {
- // Set up a display in landscape and ignoring orientation request.
- setUpDisplaySizeWithApp(2800, 1400);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-
- mActivity.mWmService.mAppCompatConfiguration.setLetterboxHorizontalPositionMultiplier(
- letterboxHorizontalPositionMultiplier);
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
- // Rotate to put activity in size compat mode.
- rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- assertTrue(mActivity.inSizeCompatMode());
- // Activity is in size compat mode but not scaled.
- assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
- }
-
private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
float letterboxVerticalPositionMultiplier) {
// Set up a display in portrait and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 08622e68629a..921228ff2a5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -78,6 +78,8 @@ import android.view.SurfaceControl;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.WmProtoLogGroups;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -183,6 +185,8 @@ public class SystemServicesTestRule implements TestRule {
}
private void setUp() {
+ ProtoLog.init(WmProtoLogGroups.values());
+
if (mOnBeforeServicesCreated != null) {
mOnBeforeServicesCreated.run();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 65a6a69fc45e..dafa96f91812 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -51,6 +51,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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.Mockito.clearInvocations;
@@ -62,6 +63,7 @@ import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Binder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
@@ -1066,6 +1068,98 @@ public class TaskFragmentTest extends WindowTestsBase {
Math.min(outConfig.screenWidthDp, outConfig.screenHeightDp));
}
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testSetAdjacentTaskFragments() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf0 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf1 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf2 = createTaskFragmentWithActivity(task);
+ final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
+ assertFalse(tf0.hasAdjacentTaskFragment());
+
+ tf0.setAdjacentTaskFragments(adjacentTfs);
+
+ assertSame(adjacentTfs, tf0.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
+ assertTrue(tf0.hasAdjacentTaskFragment());
+ assertTrue(tf1.hasAdjacentTaskFragment());
+ assertTrue(tf2.hasAdjacentTaskFragment());
+
+ final TaskFragment.AdjacentSet adjacentTfs2 = new TaskFragment.AdjacentSet(tf0, tf1);
+ tf0.setAdjacentTaskFragments(adjacentTfs2);
+
+ assertSame(adjacentTfs2, tf0.getAdjacentTaskFragments());
+ assertSame(adjacentTfs2, tf1.getAdjacentTaskFragments());
+ assertNull(tf2.getAdjacentTaskFragments());
+ assertTrue(tf0.hasAdjacentTaskFragment());
+ assertTrue(tf1.hasAdjacentTaskFragment());
+ assertFalse(tf2.hasAdjacentTaskFragment());
+ }
+
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testClearAdjacentTaskFragments() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf0 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf1 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf2 = createTaskFragmentWithActivity(task);
+ final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
+ tf0.setAdjacentTaskFragments(adjacentTfs);
+
+ tf0.clearAdjacentTaskFragments();
+
+ assertNull(tf0.getAdjacentTaskFragments());
+ assertNull(tf1.getAdjacentTaskFragments());
+ assertNull(tf2.getAdjacentTaskFragments());
+ assertFalse(tf0.hasAdjacentTaskFragment());
+ assertFalse(tf1.hasAdjacentTaskFragment());
+ assertFalse(tf2.hasAdjacentTaskFragment());
+ }
+
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testRemoveFromAdjacentTaskFragments() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf0 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf1 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf2 = createTaskFragmentWithActivity(task);
+ final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
+ tf0.setAdjacentTaskFragments(adjacentTfs);
+
+ tf0.removeFromAdjacentTaskFragments();
+
+ assertNull(tf0.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
+ assertFalse(adjacentTfs.contains(tf0));
+ assertTrue(tf1.isAdjacentTo(tf2));
+ assertTrue(tf2.isAdjacentTo(tf1));
+ assertFalse(tf1.isAdjacentTo(tf0));
+ assertFalse(tf0.isAdjacentTo(tf1));
+ assertFalse(tf0.isAdjacentTo(tf0));
+ assertFalse(tf1.isAdjacentTo(tf1));
+ }
+
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testRemoveFromAdjacentTaskFragmentsWhenRemove() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf0 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf1 = createTaskFragmentWithActivity(task);
+ final TaskFragment tf2 = createTaskFragmentWithActivity(task);
+ final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
+ tf0.setAdjacentTaskFragments(adjacentTfs);
+
+ tf0.removeImmediately();
+
+ assertNull(tf0.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
+ assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
+ assertFalse(adjacentTfs.contains(tf0));
+ }
+
private WindowState createAppWindow(ActivityRecord app, String name) {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 29f48b86a375..f145b40d2292 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -65,33 +65,27 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
public void testAppRemoved() {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mCache.putSnapshot(window.getTask(), createSnapshot());
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onAppRemoved(window.mActivityRecord);
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
@Test
public void testAppDied() {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mCache.putSnapshot(window.getTask(), createSnapshot());
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onAppDied(window.mActivityRecord);
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
@Test
public void testTaskRemoved() {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mCache.putSnapshot(window.getTask(), createSnapshot());
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onIdRemoved(window.getTask().mTaskId);
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
@Test
@@ -99,16 +93,14 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
// Load it from disk
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- true /* restoreFromDisk */, true /* isLowResolution */));
+ assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+ true /* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
// Make sure it's not in the cache now.
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
@Test
@@ -116,20 +108,20 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
// Load it from disk
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- true /* restoreFromDisk */, false /* isLowResolution */));
+ assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+ false/* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
}
@Test
public void testClearCache() {
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mCache.putSnapshot(window.getTask(), mSnapshot);
- assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId, 0, false, false));
+ assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId,
+ false /* isLowResolution */));
mCache.clearRunningCache();
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0, false, false));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 7432537902a0..9bde0663d4a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -129,23 +129,20 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
// Attempt to load the low-res snapshot from the disk
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- true /* restoreFromDisk */, true /* isLowResolution */));
+ assertNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+ true/* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
// Load the high-res (default) snapshot from disk
- assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- true /* restoreFromDisk */, false /* isLowResolution */));
+ assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+ false /* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
// Make sure it's not in the cache now.
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, true /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, true /* isLowResolution */));
// Make sure it's not in the cache now.
- assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
- false /* restoreFromDisk */, false /* isLowResolution */));
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
}
}
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 e4512c31069a..1febc9fb4742 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -527,6 +527,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testHandlesOrientationChangeFromDescendant() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task rootTask = createTask(mDisplayContent,
WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
@@ -1570,6 +1571,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testNotSpecifyOrientationByFloatingTask() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true).setCreateParentTask(true).build();
final ActivityRecord activity = task.getTopMostActivity();
@@ -1589,6 +1591,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
@@ -1625,6 +1628,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testTaskOrientationOnDisplayWindowingModeChange() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
// Skip unnecessary operations to speed up the test.
mAtm.deferWindowLayout();
final Task task = getTestTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 039a3ddd3e4f..78f32c1a4f88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1333,6 +1333,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testDeferRotationForTransientLaunch() {
+ mDisplayContent.setIgnoreOrientationRequest(false);
final TestTransitionPlayer player = registerTestTransitionPlayer();
assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index 42752c326615..f1180ff93edb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -314,6 +314,7 @@ public class TransparentPolicyTest extends WindowTestsBase {
runTestScenario((robot) -> {
robot.transparentActivity((ta) -> {
ta.applyOnActivity((a) -> {
+ a.setIgnoreOrientationRequest(false);
a.applyToTopActivity((topActivity) -> {
topActivity.mWmService.mAppCompatConfiguration
.setLetterboxHorizontalPositionMultiplier(1.0f);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index d183cf720491..f3a2e86fe6db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -46,6 +46,7 @@ import android.app.servertransaction.WindowContextInfoChangeItem;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
@@ -54,7 +55,12 @@ import android.window.WindowTokenClient;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -65,6 +71,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+
/**
* Build/Install/Run:
* atest WmTests:WindowContextListenerControllerTests
@@ -86,6 +93,9 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
private WindowProcessController mWpc;
private WindowContainer<?> mContainer;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Before
public void setUp() {
initMocks(this);
@@ -341,6 +351,30 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
}
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void assertCallerCanReparentListener_returnsTrueWhenExpected() {
+ mController.registerWindowContainerListener(mWpc, mClientToken, mContainer,
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+
+ // Here there are several checks in one test as wm tests are expensive.
+
+ // Correct conditions -> returns true
+ mExpect.that(mController.assertCallerCanReparentListener(mClientToken,
+ /* callerCanManageAppTokens= */ true,
+ /* callingUid= */ mWpc.mUid,
+ /* displayId= */ DEFAULT_DISPLAY + 1
+ )).isTrue();
+
+ // sameDisplayId (so, container already attached) -> returnsFalse
+ mExpect.that(mController.assertCallerCanReparentListener(
+ mClientToken,
+ /* callerCanManageAppTokens= */ true,
+ /* callingUid= */ mWpc.mUid,
+ /* displayId= */ DEFAULT_DISPLAY // <- same display ID
+ )).isFalse();
+ }
+
private static class TestWindowTokenClient extends WindowTokenClient {
private Configuration mConfiguration;
private int mDisplayId;
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 bfa6cb820a42..8b9849e1fcd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
@@ -70,6 +71,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -86,10 +88,10 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
@@ -151,9 +153,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Rule
public Expect mExpect = Expect.create();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@After
public void tearDown() {
mWm.mSensitiveContentPackages.clearBlockedApps();
@@ -1275,7 +1274,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Test
public void testInputDeviceNotifyConfigurationChanged() {
- mSetFlagsRule.enableFlags(Flags.FLAG_FILTER_IRRELEVANT_INPUT_DEVICE_CHANGE);
spyOn(mDisplayContent);
doReturn(false).when(mDisplayContent).sendNewConfiguration();
final InputDevice deviceA = mock(InputDevice.class);
@@ -1412,6 +1410,84 @@ public class WindowManagerServiceTests extends WindowTestsBase {
assertThat(result).isEqualTo(WindowManagerGlobal.ADD_INVALID_DISPLAY);
}
+ /** Mocks some deps to associate a display content to a specific display id. */
+ private void setupReparentWindowContextToDisplayAreaTest(WindowToken windowToken,
+ DisplayContent dc, int displayId) {
+ spyOn(mWm.mWindowContextListenerController);
+ doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ doReturn(true).when(mWm.mWindowContextListenerController).assertCallerCanReparentListener(
+ any(), anyBoolean(), anyInt(), eq(displayId));
+ doReturn(windowToken).when(mWm.mWindowContextListenerController).getContainer(
+ eq(windowToken.token));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContextToDisplayArea_newDisplay_reparented() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(windowToken.getDisplayContent()).isEqualTo(mDisplayContent);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ assertThat(windowToken.getDisplayContent()).isNotEqualTo(mDisplayContent);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContextToDisplayArea_newDisplayButFlagDisabled_notReparented() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isFalse();
+
+ assertThat(windowToken.getDisplayContent()).isEqualTo(mDisplayContent);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContext_afterReparent_DCNeedsLayout() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ assertThat(dc.isLayoutNeeded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContext_afterReparent_traversalScheduled() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+ spyOn(mWm.mWindowPlacerLocked);
+ reset(mWm.mWindowPlacerLocked);
+
+ verify(mWm.mWindowPlacerLocked, never()).requestTraversal();
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
+ }
+
+
class TestResultReceiver implements IResultReceiver {
public android.os.Bundle resultData;
private final IBinder mBinder = mock(IBinder.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 410fa2879600..da4c522834a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -777,6 +777,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSetIgnoreOrientationRequest_taskDisplayArea() {
removeGlobalMinSizeRestriction();
+ mDisplayContent.setIgnoreOrientationRequest(false);
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final Task rootTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
@@ -815,6 +816,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSetIgnoreOrientationRequest_displayContent() {
removeGlobalMinSizeRestriction();
+ mDisplayContent.setIgnoreOrientationRequest(false);
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final Task rootTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
@@ -924,6 +926,49 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
}
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testSetAdjacentLaunchRootSet() {
+ final DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+
+ final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
+ final RunningTaskInfo info1 = task1.getTaskInfo();
+ final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
+ final RunningTaskInfo info2 = task2.getTaskInfo();
+ final Task task3 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
+ final RunningTaskInfo info3 = task3.getTaskInfo();
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setAdjacentRootSet(info1.token, info2.token, info3.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertTrue(task1.hasAdjacentTaskFragment());
+ assertTrue(task2.hasAdjacentTaskFragment());
+ assertTrue(task3.hasAdjacentTaskFragment());
+ assertTrue(task1.isAdjacentTo(task2));
+ assertTrue(task1.isAdjacentTo(task3));
+ assertTrue(task2.isAdjacentTo(task3));
+
+ wct = new WindowContainerTransaction();
+ wct.clearAdjacentRoots(info1.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertFalse(task1.hasAdjacentTaskFragment());
+ assertTrue(task2.hasAdjacentTaskFragment());
+ assertTrue(task3.hasAdjacentTaskFragment());
+ assertFalse(task1.isAdjacentTo(task2));
+ assertFalse(task1.isAdjacentTo(task3));
+ assertTrue(task2.isAdjacentTo(task3));
+
+ wct = new WindowContainerTransaction();
+ wct.clearAdjacentRoots(info2.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertFalse(task2.hasAdjacentTaskFragment());
+ assertFalse(task3.hasAdjacentTaskFragment());
+ assertFalse(task2.isAdjacentTo(task3));
+ }
+
@Test
public void testTileAddRemoveChild() {
final StubOrganizer listener = new StubOrganizer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 78e6cbf9c36a..b61dada809d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -89,6 +89,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
+import android.tools.function.Supplier;
import android.util.MergedConfiguration;
import android.util.SparseArray;
import android.view.Display;
@@ -201,14 +202,10 @@ public class WindowTestsBase extends SystemServiceTestsBase {
* {@link WindowTestsBase#setUpBase()}.
*/
private static boolean sGlobalOverridesChecked;
+
/**
* Whether device-specific overrides have already been checked in
- * {@link WindowTestsBase#setUpBase()} when the default display is used.
- */
- private static boolean sOverridesCheckedDefaultDisplay;
- /**
- * Whether device-specific overrides have already been checked in
- * {@link WindowTestsBase#setUpBase()} when a {@link TestDisplayContent} is used.
+ * {@link WindowTestsBase#setUpBase()}.
*/
private static boolean sOverridesCheckedTestDisplay;
@@ -332,17 +329,14 @@ public class WindowTestsBase extends SystemServiceTestsBase {
private void checkDeviceSpecificOverridesNotApplied() {
// Check global overrides
if (!sGlobalOverridesChecked) {
+ sGlobalOverridesChecked = true;
assertEquals(0, mWm.mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio(),
0 /* delta */);
- sGlobalOverridesChecked = true;
}
// Check display-specific overrides
- if (!sOverridesCheckedDefaultDisplay && mDisplayContent == mDefaultDisplay) {
- assertFalse(mDisplayContent.getIgnoreOrientationRequest());
- sOverridesCheckedDefaultDisplay = true;
- } else if (!sOverridesCheckedTestDisplay && mDisplayContent instanceof TestDisplayContent) {
- assertFalse(mDisplayContent.getIgnoreOrientationRequest());
+ if (!sOverridesCheckedTestDisplay) {
sOverridesCheckedTestDisplay = true;
+ assertFalse(mDisplayContent.mHasSetIgnoreOrientationRequest);
}
}
@@ -1121,7 +1115,7 @@ public class WindowTestsBase extends SystemServiceTestsBase {
displayContent.getDisplayRotation().configure(width, height);
final Configuration c = new Configuration();
displayContent.computeScreenConfiguration(c);
- displayContent.onRequestedOverrideConfigurationChanged(c);
+ displayContent.performDisplayOverrideConfigUpdate(c);
}
static void makeDisplayLargeScreen(DisplayContent displayContent) {
@@ -1811,6 +1805,124 @@ public class WindowTestsBase extends SystemServiceTestsBase {
}
}
+ protected WindowStateBuilder newWindowBuilder(String name, int type) {
+ return new WindowStateBuilder(name, type, mWm, mDisplayContent, mIWindow,
+ this::getTestSession, this::createWindowToken);
+ }
+
+ /**
+ * Builder for creating new window.
+ */
+ protected static class WindowStateBuilder {
+ private final String mName;
+ private final int mType;
+ private final WindowManagerService mWm;
+ private final DisplayContent mDefaultTargetDisplay;
+ private final Supplier<WindowToken, Session> mSessionSupplier;
+ private final WindowTokenCreator mWindowTokenCreator;
+
+ private int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private IWindow mClientWindow;
+ private boolean mOwnerCanAddInternalSystemWindow = false;
+ private int mOwnerId = 0;
+ private WindowState mParent;
+ private DisplayContent mTargetDisplay;
+ private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ private WindowToken mWindowToken;
+
+ WindowStateBuilder(String name, int type, WindowManagerService windowManagerService,
+ DisplayContent dc, IWindow iWindow, Supplier<WindowToken, Session> sessionSupplier,
+ WindowTokenCreator windowTokenCreator) {
+ mName = name;
+ mType = type;
+ mClientWindow = iWindow;
+ mDefaultTargetDisplay = dc;
+ mSessionSupplier = sessionSupplier;
+ mWindowTokenCreator = windowTokenCreator;
+ mWm = windowManagerService;
+ }
+
+ WindowStateBuilder setActivityType(int activityType) {
+ mActivityType = activityType;
+ return this;
+ }
+
+ WindowStateBuilder setClientWindow(IWindow clientWindow) {
+ mClientWindow = clientWindow;
+ return this;
+ }
+
+ WindowStateBuilder setDisplay(DisplayContent displayContent) {
+ mTargetDisplay = displayContent;
+ return this;
+ }
+
+ WindowStateBuilder setOwnerCanAddInternalSystemWindow(
+ boolean ownerCanAddInternalSystemWindow) {
+ mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
+ return this;
+ }
+
+ WindowStateBuilder setOwnerId(int ownerId) {
+ mOwnerId = ownerId;
+ return this;
+ }
+
+ WindowStateBuilder setParent(WindowState parent) {
+ mParent = parent;
+ return this;
+ }
+
+ WindowStateBuilder setWindowToken(WindowToken token) {
+ mWindowToken = token;
+ return this;
+ }
+
+ WindowStateBuilder setWindowingMode(int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
+ WindowState build() {
+ SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
+
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(mType);
+ attrs.setTitle(mName);
+ attrs.packageName = "test";
+
+ assertFalse(
+ "targetDisplay shouldn't be specified together with windowToken, since"
+ + " windowToken will be derived from targetDisplay.",
+ mWindowToken != null && mTargetDisplay != null);
+
+ if (mWindowToken == null) {
+ if (mTargetDisplay != null) {
+ mWindowToken = mWindowTokenCreator.createWindowToken(mTargetDisplay,
+ mWindowingMode, mActivityType, mType);
+ } else if (mParent != null) {
+ mWindowToken = mParent.mToken;
+ } else {
+ // Use default mDisplayContent as window token.
+ mWindowToken = mWindowTokenCreator.createWindowToken(mDefaultTargetDisplay,
+ mWindowingMode, mActivityType, mType);
+ }
+ }
+
+ final WindowState w = new WindowState(mWm, mSessionSupplier.get(mWindowToken),
+ mClientWindow, mWindowToken, mParent, OP_NONE, attrs, VISIBLE, mOwnerId,
+ UserHandle.getUserId(mOwnerId), mOwnerCanAddInternalSystemWindow);
+ // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
+ // adding it to the token...
+ mWindowToken.addWindow(w);
+ return w;
+ }
+
+ interface WindowTokenCreator {
+ WindowToken createWindowToken(DisplayContent dc, int windowingMode, int activityType,
+ int type);
+ }
+ }
+
static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer {
private final ActivityTaskManagerService mAtm;
private final WindowManagerService mWMService;
@@ -2039,9 +2151,22 @@ public class WindowTestsBase extends SystemServiceTestsBase {
return new TestWindowToken(type, dc, persistOnEmpty);
}
+ static TestWindowToken createTestClientWindowToken(int type, DisplayContent dc) {
+ SystemServicesTestRule.checkHoldsLock(dc.mWmService.mGlobalLock);
+
+ return new TestWindowToken(type, dc, false /* persistOnEmpty */, true /* fromClient */);
+ }
+
/** Used so we can gain access to some protected members of the {@link WindowToken} class */
static class TestWindowToken extends WindowToken {
+ private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty,
+ boolean fromClient) {
+ super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+ false /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+ fromClient /* fromClientToken */, null /* options */);
+ }
+
private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
false /* ownerCanManageAppTokens */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 35328a0e1dc0..f226b9d29ca0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -33,21 +33,27 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets;
import android.window.WindowContext;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
import java.util.function.BiFunction;
@@ -304,14 +310,39 @@ public class WindowTokenTests extends WindowTestsBase {
// immediately. verify the window will hide without applying exit animation.
mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */,
mDisplayContent.mDisplayId);
- verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */);
+ verify(win).onSetAppExiting(eq(false) /* animateExit */);
verify(win).hide(false /* doAnimation */, false /* requestAnim */);
assertFalse(win.isOnScreen());
- verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ verify(win.mWinAnimator, never()).applyAnimationLocked(TRANSIT_EXIT, false);
assertTrue(win.mToken.hasChild());
// Even though the window is being removed afterwards, it won't apply exit animation.
win.removeIfPossible();
- verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ verify(win.mWinAnimator, never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void onDisplayChanged_differentDisplay_reparented() {
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ final DisplayContent dc = mock(DisplayContent.class);
+ when(dc.getWindowToken(any())).thenReturn(null); // dc doesn't have this window token.
+
+ token.onDisplayChanged(dc);
+
+ verify(dc).reParentWindowToken(eq(token));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void onDisplayChanged_samedisplay_notReparented() {
+
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ final DisplayContent dc = mock(DisplayContent.class);
+ when(dc.getWindowToken(any())).thenReturn(mock(WindowToken.class));
+
+ token.onDisplayChanged(dc);
+
+ verify(dc, never()).reParentWindowToken(eq(token));
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 010a32274dcd..e7c9e927b311 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -48,6 +48,7 @@ import android.app.IUidObserver;
import android.app.PendingIntent;
import android.app.UidObserver;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.supervision.SupervisionManagerInternal;
import android.app.usage.AppLaunchEstimateInfo;
import android.app.usage.AppStandbyInfo;
import android.app.usage.BroadcastResponseStatsList;
@@ -230,6 +231,7 @@ public class UsageStatsService extends SystemService implements
// Do not use directly. Call getDpmInternal() instead
DevicePolicyManagerInternal mDpmInternal;
// Do not use directly. Call getShortcutServiceInternal() instead
+ SupervisionManagerInternal mSupervisionManagerInternal;
ShortcutServiceInternal mShortcutServiceInternal;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
@@ -439,6 +441,9 @@ public class UsageStatsService extends SystemService implements
// initialize mDpmInternal
getDpmInternal();
// initialize mShortcutServiceInternal
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ getSupervisionManagerInternal();
+ }
getShortcutServiceInternal();
mResponseStatsTracker.onSystemServicesReady(getContext());
@@ -604,6 +609,15 @@ public class UsageStatsService extends SystemService implements
return mDpmInternal;
}
+ @Nullable
+ private SupervisionManagerInternal getSupervisionManagerInternal() {
+ if (mSupervisionManagerInternal == null) {
+ mSupervisionManagerInternal =
+ LocalServices.getService(SupervisionManagerInternal.class);
+ }
+ return mSupervisionManagerInternal;
+ }
+
private ShortcutServiceInternal getShortcutServiceInternal() {
if (mShortcutServiceInternal == null) {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
@@ -753,6 +767,16 @@ public class UsageStatsService extends SystemService implements
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED);
}
+ private boolean isSupervisionEnabled(int callingUid) {
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ SupervisionManagerInternal smInternal = getSupervisionManagerInternal();
+ return smInternal != null && smInternal.isActiveSupervisionApp(callingUid);
+ } else {
+ DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+ return dpmInternal != null && dpmInternal.isActiveSupervisionApp(callingUid);
+ }
+ }
+
private static void deleteRecursively(final File path) {
if (path.isDirectory()) {
final File[] files = path.listFiles();
@@ -2929,10 +2953,9 @@ public class UsageStatsService extends SystemService implements
long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent,
String callingPackage) {
final int callingUid = Binder.getCallingUid();
- final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(
Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
- && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ && !isSupervisionEnabled(callingUid)) {
throw new SecurityException("Caller must be the active supervision app or "
+ "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
@@ -2956,10 +2979,9 @@ public class UsageStatsService extends SystemService implements
@Override
public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
final int callingUid = Binder.getCallingUid();
- final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(
Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
- && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ && !isSupervisionEnabled(callingUid)) {
throw new SecurityException("Caller must be the active supervision app or "
+ "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index c508fa968b14..ce3cd29a3ce2 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -26,6 +26,7 @@ import android.util.Slog;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.audio.AudioService;
+import com.android.server.usb.flags.Flags;
import java.util.Arrays;
@@ -211,6 +212,9 @@ public final class UsbAlsaDevice {
mIsSelected[direction] = true;
mState[direction] = 0;
startJackDetect();
+ if (direction == OUTPUT && Flags.maximizeUsbAudioVolumeWhenConnecting()) {
+ nativeSetVolume(mCardNum, 1.0f /*volume*/);
+ }
updateWiredDeviceConnectionState(direction, true /*enable*/);
}
@@ -412,5 +416,7 @@ public final class UsbAlsaDevice {
return result;
}
+
+ private native void nativeSetVolume(int card, float volume);
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 15c8b135d2c4..c65f7844f201 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -70,6 +70,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
@@ -161,6 +162,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private static final String MIDI_ALSA_PATH =
"/sys/class/android_usb/android0/f_midi/alsa";
+ /**
+ * The minimum SELinux genfs labels version that supports udc sysfs genfs context.
+ */
+ private static final int MIN_SELINUX_GENFS_LABELS_VERSION = 202404;
+
private static final int MSG_UPDATE_STATE = 0;
private static final int MSG_ENABLE_ADB = 1;
private static final int MSG_SET_CURRENT_FUNCTIONS = 2;
@@ -445,7 +451,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mEnableUdcSysfsUsbStateUpdate =
android.hardware.usb.flags.Flags.enableUdcSysfsUsbStateUpdate()
- && context.getResources().getBoolean(R.bool.config_enableUdcSysfsUsbStateUpdate);
+ && context.getResources().getBoolean(R.bool.config_enableUdcSysfsUsbStateUpdate)
+ && SELinux.getGenfsLabelsVersion() > MIN_SELINUX_GENFS_LABELS_VERSION;
if (mEnableUdcSysfsUsbStateUpdate) {
mUEventObserver.startObserving(UDC_SUBSYS_MATCH);
diff --git a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
index a2d0efd1d063..dfbd74c1f3e1 100644
--- a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
+++ b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
@@ -21,3 +21,10 @@ flag {
description: "This flag checks if phone is unlocked after boot"
bug: "73654179"
}
+
+flag {
+ name: "maximize_usb_audio_volume_when_connecting"
+ namespace: "usb"
+ description: "This flag maximizes the usb audio volume when it is connected"
+ bug: "245041322"
+}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index a69dfb0b255f..cdb3eaf46def 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -16,12 +16,12 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -255,6 +255,11 @@ public class ParcelableCallAnalytics implements Parcelable {
public static final int CALLTYPE_OUTGOING = 2;
// Constants for call technology
+ /**
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_PHONE = 0x1;
public static final int GSM_PHONE = 0x2;
public static final int IMS_PHONE = 0x4;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d89c9c16eb09..7082f0028a5e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2049,11 +2049,15 @@ public class TelecomManager {
/**
* Ends the foreground call on the device.
* <p>
- * If there is a ringing call, calling this method rejects the ringing call. Otherwise the
+ * If there is a ringing call, calling this method rejects the ringing call. Otherwise, the
* foreground call is ended.
* <p>
* Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
* if an attempt is made to end an emergency call.
+ * <p>
+ * Note: If the foreground call on this device is self-managed, this method will only end
+ * the call if the caller of this method is privileged (i.e. system, shell, or root) or system
+ * UI.
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
@@ -2082,6 +2086,9 @@ public class TelecomManager {
* the incoming call requests. This means, for example, that an incoming call requesting
* {@link VideoProfile#STATE_BIDIRECTIONAL} will be answered, accepting that state.
*
+ * If the ringing incoming call is self-managed, this method will only accept the call if the
+ * caller of this method is privileged (i.e. system, shell, or root) or system UI.
+ *
* @deprecated Companion apps for wearable devices should use the {@link InCallService} API
* instead.
*/
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
index eda0f5b24958..c4a3670e4f14 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
@@ -24,5 +24,5 @@ import com.android.internal.telecom.IInternalServiceRetriever;
* Allows the TelecomLoaderService to pass additional dependencies required for creation.
*/
interface ITelecomLoader {
- ITelecomService createTelecomService(IInternalServiceRetriever retriever);
+ ITelecomService createTelecomService(IInternalServiceRetriever retriever, String sysUiName);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 59cb5ff37874..fa4ec1692b0e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -317,8 +317,10 @@ public class CarrierConfigManager {
* If this is set as false and the supplementary service menu is visible, the associated setting
* will be enabled and disabled based on the availability of supplementary services over UT. See
* {@link #KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_SUPPORT_SS_OVER_CDMA_BOOL = "support_ss_over_cdma_bool";
/**
@@ -536,7 +538,11 @@ public class CarrierConfigManager {
*/
public static final String KEY_4G_ONLY_BOOL = "4g_only_bool";
- /** Show cdma network mode choices 1x, 3G, global etc. */
+ /** Show cdma network mode choices 1x, 3G, global etc.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
/** CDMA activation goes through HFA */
@@ -544,9 +550,12 @@ public class CarrierConfigManager {
/**
* CDMA activation goes through OTASP.
+ * @deprecated Legacy CDMA is unsupported.
*/
// TODO: This should be combined with config_use_hfa_for_provisioning and implemented as an enum
// (NONE, HFA, OTASP).
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL =
"use_otasp_for_provisioning_bool";
@@ -556,10 +565,20 @@ public class CarrierConfigManager {
/** Does not display additional call setting for IMS phone based on GSM Phone */
public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
- /** Show APN Settings for some CDMA carriers */
+ /**
+ * Show APN Settings for some CDMA carriers
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
- /** After a CDMA conference call is merged, the swap button should be displayed. */
+ /**
+ * After a CDMA conference call is merged, the swap button should be displayed.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
/**
@@ -597,7 +616,10 @@ public class CarrierConfigManager {
/**
* Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or is
* potentially harmful by locking the SIM to 3G.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL =
"disable_cdma_activation_code_bool";
@@ -675,14 +697,20 @@ public class CarrierConfigManager {
/**
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String
KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
/**
* Override the platform's notion of a network operator being considered non roaming.
* Value is string array of SIDs to be considered not roaming for 3GPP2 RATs.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String
KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
@@ -1243,8 +1271,10 @@ public class CarrierConfigManager {
/**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CARRIER_ERI_FILE_NAME_STRING = "carrier_eri_file_name_string";
/* The following 3 fields are related to carrier visual voicemail. */
@@ -1386,7 +1416,10 @@ public class CarrierConfigManager {
* Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
* non-zero value is specified, the UE shall wait for the specified amount of time before it
* sends out successive DTMF tones on the network.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
/**
@@ -1804,8 +1837,10 @@ public class CarrierConfigManager {
* If this bit is not set, the carrier name display string will be selected from the carrier
* display name resolver which doesn't apply the ERI rules.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_ALLOW_ERI_BOOL = "allow_cdma_eri_bool";
/**
@@ -1849,8 +1884,10 @@ public class CarrierConfigManager {
* If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
* will be #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING. If false, or if phone type is not
* CDMA/CDMA-LTE or if roaming, then #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING will be ignored.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL =
"cdma_home_registered_plmn_name_override_bool";
@@ -1858,8 +1895,10 @@ public class CarrierConfigManager {
* String to identify registered PLMN name in CarrierConfig app. This string overrides
* registered PLMN name if #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL is true, phone type
* is CDMA/CDMA-LTE and device is not in roaming state; otherwise, it will be ignored.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING =
"cdma_home_registered_plmn_name_string";
@@ -2440,7 +2479,10 @@ public class CarrierConfigManager {
* For carriers which require an empty flash to be sent before sending the normal 3-way calling
* flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
* flash is sent.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
/**
@@ -2454,14 +2496,21 @@ public class CarrierConfigManager {
* @see TelephonyManager#CDMA_ROAMING_MODE_HOME
* @see TelephonyManager#CDMA_ROAMING_MODE_AFFILIATED
* @see TelephonyManager#CDMA_ROAMING_MODE_ANY
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
/**
* Determines whether 1X voice calls is supported for some CDMA carriers.
* Default value is true.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL =
"support_cdma_1x_voice_calls_bool";
@@ -2483,8 +2532,10 @@ public class CarrierConfigManager {
/**
* Report IMEI as device id even if it's a CDMA/LTE phone.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_FORCE_IMEI_BOOL = "force_imei_bool";
/**
@@ -3217,8 +3268,10 @@ public class CarrierConfigManager {
* on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and
* *82<number> will be converted to *31#<number> before dialing a call when this key is
* set TRUE and device is roaming on a 3GPP network.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL =
"convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool";
@@ -3621,8 +3674,11 @@ public class CarrierConfigManager {
/**
* Support for the original string display of CDMA MO call.
* By default, it is disabled.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL =
"config_show_orig_dial_string_for_cdma";
@@ -5070,8 +5126,10 @@ public class CarrierConfigManager {
* The default values come from 3GPP2 C.R1001 table 8.1-1.
* Enhanced Roaming Indicator Number Assignments
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY =
"cdma_enhanced_roaming_indicator_for_home_network_int_array";
@@ -9756,9 +9814,8 @@ public class CarrierConfigManager {
* }</pre>
* <p>
* This config is empty by default.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE =
"regional_satellite_earfcn_bundle";
@@ -9885,15 +9942,14 @@ public class CarrierConfigManager {
"remove_satellite_plmn_in_manual_network_scan_bool";
/**
- * This value is used to set the max datagram size, if the value is not available then the
- * default one will be used.
- * If key is {@code true}, retrieve the max datagram value and use this value always,
- * {@code false} the default value from the modem will be used.
+ * This value is used to set the max datagram size in bytes.
+ * If the value is not available then the default value will be used.
*
- * @hide
+ * The default value is 255 bytes.
*/
- public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE =
- "satellite_sos_max_datagram_size";
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT =
+ "satellite_sos_max_datagram_size_bytes_int";
/** @hide */
@IntDef({
@@ -9932,6 +9988,19 @@ public class CarrierConfigManager {
"satellite_data_support_mode_int";
/**
+ * Determines whether data roaming off setting should be ignored and satellite data should be
+ * allowed even when data roaming is off.
+ *
+ * If the carrier would like to allow the device to use satellite connection when data roaming
+ * is off, this key should be set to {@code true}.
+ *
+ * The default value is {@code false} i.e. disallow satellite data when data roaming is off.
+ */
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ public static final String KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL =
+ "satellite_ignore_data_roaming_setting_bool";
+
+ /**
* Determine whether to override roaming Wi-Fi Calling preference when device is connected to
* non-terrestrial network.
* {@code true} - roaming preference cannot be changed by user independently.
@@ -10062,9 +10131,9 @@ public class CarrierConfigManager {
/**
* The display name that will be used for satellite functionality within the UI.
- * The default string value for this is "Satellite".
- * @hide
+ * The default string value is empty string.
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_DISPLAY_NAME_STRING = "satellite_display_name_string";
/**
@@ -10183,10 +10252,8 @@ public class CarrierConfigManager {
* A string array containing the list of messaging apps that support satellite.
*
* The default value contains only "com.google.android.apps.messaging"
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY =
"satellite_supported_msg_apps_string_array";
@@ -11334,6 +11401,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
sDefaults.putInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED);
+ sDefaults.putBoolean(KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL, false);
sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
@@ -11458,7 +11526,7 @@ public class CarrierConfigManager {
sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
sDefaults.putInt(KEY_WEAR_CONNECTIVITY_BT_TO_CELL_DELAY_MS_INT, -1);
sDefaults.putInt(KEY_WEAR_CONNECTIVITY_EXTEND_BT_TO_CELL_DELAY_ON_WIFI_MS_INT, -1);
- sDefaults.putInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE, 255);
+ sDefaults.putInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT, 255);
}
/**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index 14de2f285756..60f986c684fe 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -28,6 +29,7 @@ import android.os.IBinder;
import android.os.RemoteCallback;
import android.telephony.cdma.CdmaSmsCbProgramData;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -88,9 +90,12 @@ public abstract class CellBroadcastService extends Service {
* @param slotIndex the index of the slot which received the message
* @param bearerData the CDMA SMS bearer data
* @param serviceCategory the CDMA SCPT service category
+ * @deprecated Legacy CDMA is unsupported.
*/
- public abstract void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
- @CdmaSmsCbProgramData.Category int serviceCategory);
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
+ public void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
+ @CdmaSmsCbProgramData.Category int serviceCategory) {}
/**
* Handle a CDMA cell broadcast SMS message forwarded from the system.
@@ -102,10 +107,13 @@ public abstract class CellBroadcastService extends Service {
* @param callback a callback to run after each cell broadcast receiver has handled
* the SCP message. The bundle will contain a non-separated
* dial string as and an ArrayList of {@link CdmaSmsCbProgramResults}.
+ * @deprecated Legacy CDMA is unsupported.
*/
- public abstract void onCdmaScpMessage(int slotIndex,
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
+ public void onCdmaScpMessage(int slotIndex,
@NonNull List<CdmaSmsCbProgramData> smsCbProgramData,
- @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback);
+ @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback) {}
/**
* Get broadcasted area information.
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 5eace5433128..4cb622b3eb9e 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -18,11 +18,13 @@ package android.telephony;
import static android.text.TextUtils.formatSimple;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.telephony.cdma.CdmaCellLocation;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -30,7 +32,11 @@ import java.util.Objects;
/**
* CellIdentity is to represent a unique CDMA cell
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
public final class CellIdentityCdma extends CellIdentity {
private static final String TAG = CellIdentityCdma.class.getSimpleName();
private static final boolean DBG = false;
@@ -99,20 +105,30 @@ public final class CellIdentityCdma extends CellIdentity {
*/
public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat,
@Nullable String alphal, @Nullable String alphas) {
- super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
- mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX);
- mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX);
- mBasestationId = inRangeOrUnavailable(bid, 0, BASESTATION_ID_MAX);
- lat = inRangeOrUnavailable(lat, LATITUDE_MIN, LATITUDE_MAX);
- lon = inRangeOrUnavailable(lon, LONGITUDE_MIN, LONGITUDE_MAX);
-
- if (!isNullIsland(lat, lon)) {
- mLongitude = lon;
- mLatitude = lat;
+ super(TAG, CellInfo.TYPE_CDMA, null, null, Flags.cleanupCdma() ? null : alphal,
+ Flags.cleanupCdma() ? null : alphas);
+ if (Flags.cleanupCdma()) {
+ mNetworkId = CellInfo.UNAVAILABLE;
+ mSystemId = CellInfo.UNAVAILABLE;
+ mBasestationId = CellInfo.UNAVAILABLE;
+ mLongitude = CellInfo.UNAVAILABLE;
+ mLatitude = CellInfo.UNAVAILABLE;
+ mGlobalCellId = null;
} else {
- mLongitude = mLatitude = CellInfo.UNAVAILABLE;
+ mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX);
+ mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX);
+ mBasestationId = inRangeOrUnavailable(bid, 0, BASESTATION_ID_MAX);
+ lat = inRangeOrUnavailable(lat, LATITUDE_MIN, LATITUDE_MAX);
+ lon = inRangeOrUnavailable(lon, LONGITUDE_MIN, LONGITUDE_MAX);
+
+ if (!isNullIsland(lat, lon)) {
+ mLongitude = lon;
+ mLatitude = lat;
+ } else {
+ mLongitude = mLatitude = CellInfo.UNAVAILABLE;
+ }
+ updateGlobalCellId();
}
- updateGlobalCellId();
}
private CellIdentityCdma(@NonNull CellIdentityCdma cid) {
@@ -124,7 +140,11 @@ public final class CellIdentityCdma extends CellIdentity {
return new CellIdentityCdma(this);
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellIdentityCdma sanitizeLocationInfo() {
return new CellIdentityCdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
@@ -157,7 +177,11 @@ public final class CellIdentityCdma extends CellIdentity {
/**
* @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getNetworkId() {
return mNetworkId;
}
@@ -165,7 +189,11 @@ public final class CellIdentityCdma extends CellIdentity {
/**
* @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getSystemId() {
return mSystemId;
}
@@ -173,7 +201,10 @@ public final class CellIdentityCdma extends CellIdentity {
/**
* @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getBasestationId() {
return mBasestationId;
}
@@ -184,7 +215,11 @@ public final class CellIdentityCdma extends CellIdentity {
* of 0.25 seconds and ranges from -2592000 to 2592000, both
* values inclusive (corresponding to a range of -180
* to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getLongitude() {
return mLongitude;
}
@@ -195,7 +230,11 @@ public final class CellIdentityCdma extends CellIdentity {
* of 0.25 seconds and ranges from -1296000 to 1296000, both
* values inclusive (corresponding to a range of -90
* to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getLatitude() {
return mLatitude;
}
@@ -206,7 +245,11 @@ public final class CellIdentityCdma extends CellIdentity {
super.hashCode());
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@NonNull
@Override
public CdmaCellLocation asCellLocation() {
@@ -267,17 +310,43 @@ public final class CellIdentityCdma extends CellIdentity {
/** Construct from Parcel, type has already been processed */
private CellIdentityCdma(Parcel in) {
super(TAG, CellInfo.TYPE_CDMA, in);
- mNetworkId = in.readInt();
- mSystemId = in.readInt();
- mBasestationId = in.readInt();
- mLongitude = in.readInt();
- mLatitude = in.readInt();
-
- updateGlobalCellId();
- if (DBG) log(toString());
+
+ if (Flags.cleanupCdma()) {
+ in.readInt();
+ mNetworkId = CellInfo.UNAVAILABLE;
+
+ in.readInt();
+ mSystemId = CellInfo.UNAVAILABLE;
+
+ in.readInt();
+ mBasestationId = CellInfo.UNAVAILABLE;
+
+ in.readInt();
+ mLongitude = CellInfo.UNAVAILABLE;
+
+ in.readInt();
+ mLatitude = CellInfo.UNAVAILABLE;
+
+ mGlobalCellId = null;
+ } else {
+ mNetworkId = in.readInt();
+ mSystemId = in.readInt();
+ mBasestationId = in.readInt();
+ mLongitude = in.readInt();
+ mLatitude = in.readInt();
+
+ updateGlobalCellId();
+ if (DBG) log(toString());
+ }
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ *
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressWarnings("hiding")
public static final @android.annotation.NonNull Creator<CellIdentityCdma> CREATOR =
new Creator<CellIdentityCdma>() {
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index aa8cff52bcaf..3c4bb5164ffc 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -16,17 +16,23 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
/**
* A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
public final class CellInfoCdma extends CellInfo implements Parcelable {
private static final String LOG_TAG = "CellInfoCdma";
@@ -61,7 +67,10 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
/**
* @return a {@link CellIdentityCdma} instance.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellIdentityCdma getCellIdentity() {
return mCellIdentityCdma;
@@ -75,7 +84,10 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
/**
* @return a {@link CellSignalStrengthCdma} instance.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellSignalStrengthCdma getCellSignalStrength() {
return mCellSignalStrengthCdma;
@@ -135,7 +147,12 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
return 0;
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags, TYPE_CDMA);
@@ -154,7 +171,12 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
if (DBG) log("CellInfoCdma(Parcel): " + toString());
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final @android.annotation.NonNull Creator<CellInfoCdma> CREATOR = new Creator<CellInfoCdma>() {
@Override
public CellInfoCdma createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 5298e67bdf80..12a7294c42de 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.util.Objects;
@@ -68,13 +69,17 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
*/
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0);
- mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0);
- mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0);
- mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0);
- mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8);
+ if (Flags.cleanupCdma()) {
+ setDefaultValues();
+ } else {
+ mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0);
+ mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0);
+ mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0);
+ mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0);
+ mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8);
- updateLevel(null, null);
+ updateLevel(null, null);
+ }
}
/** @hide */
@@ -84,6 +89,10 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
/** @hide */
protected void copyFrom(CellSignalStrengthCdma s) {
+ if (Flags.cleanupCdma()) {
+ setDefaultValues();
+ return;
+ }
mCdmaDbm = s.mCdmaDbm;
mCdmaEcio = s.mCdmaEcio;
mEvdoDbm = s.mEvdoDbm;
@@ -389,6 +398,7 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
/** @hide */
@Override
public boolean isValid() {
+ if (Flags.cleanupCdma()) return false;
return !this.equals(sInvalid);
}
@@ -446,7 +456,12 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
mEvdoEcio = in.readInt();
mEvdoSnr = in.readInt();
mLevel = in.readInt();
- if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
+
+ if (Flags.cleanupCdma()) {
+ setDefaultValues();
+ } else {
+ if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
+ }
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index d9437ab29881..2d650ab20802 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -255,25 +255,75 @@ public final class PreciseDisconnectCause {
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
public static final int EMERGENCY_PERM_FAILURE = 326;
- /** Mobile station (MS) is locked until next power cycle. */
+ /**
+ * Mobile station (MS) is locked until next power cycle.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
- /** Drop call. */
+ /**
+ * Drop call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_DROP = 1001;
- /** INTERCEPT order received, Mobile station (MS) state idle entered. */
+ /**
+ * INTERCEPT order received, Mobile station (MS) state idle entered.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_INTERCEPT = 1002;
- /** Mobile station (MS) has been redirected, call is cancelled. */
+ /**
+ * Mobile station (MS) has been redirected, call is cancelled.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_REORDER = 1003;
- /** Service option rejection. */
+ /**
+ * Service option rejection.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_SO_REJECT = 1004;
- /** Requested service is rejected, retry delay is set. */
+ /**
+ * Requested service is rejected, retry delay is set.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_RETRY_ORDER = 1005;
- /** Unable to obtain access to the CDMA system. */
+ /**
+ * Unable to obtain access to the CDMA system.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ACCESS_FAILURE = 1006;
- /** Not a preempted call. */
+ /**
+ * Not a preempted call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_PREEMPTED = 1007;
- /** Not an emergency call. */
+ /**
+ * Not an emergency call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_NOT_EMERGENCY = 1008;
- /** Access Blocked by CDMA network. */
+ /**
+ * Access Blocked by CDMA network.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ACCESS_BLOCKED = 1009;
/* OEM specific error codes. To be used by OEMs when they don't want to
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index e01b10eed4db..bb4ce6e787de 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -16,12 +16,15 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.OverrideNetworkType;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -94,6 +97,12 @@ public final class TelephonyDisplayInfo implements Parcelable {
private final boolean mIsRoaming;
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ private final boolean mIsNtn;
+
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ private final boolean mIsSatelliteConstrainedData;
+
/**
* Constructor
*
@@ -106,7 +115,7 @@ public final class TelephonyDisplayInfo implements Parcelable {
@Deprecated
public TelephonyDisplayInfo(@NetworkType int networkType,
@OverrideNetworkType int overrideNetworkType) {
- this(networkType, overrideNetworkType, false);
+ this(networkType, overrideNetworkType, false, false, false);
}
/**
@@ -118,12 +127,37 @@ public final class TelephonyDisplayInfo implements Parcelable {
*
* @hide
*/
+ @Deprecated
public TelephonyDisplayInfo(@NetworkType int networkType,
@OverrideNetworkType int overrideNetworkType,
boolean isRoaming) {
mNetworkType = networkType;
mOverrideNetworkType = overrideNetworkType;
mIsRoaming = isRoaming;
+ mIsNtn = false;
+ mIsSatelliteConstrainedData = false;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param networkType Current packet-switching cellular network type
+ * @param overrideNetworkType The override network type
+ * @param isRoaming True if the device is roaming after override.
+ * @param isNtn True if the device is camped to non-terrestrial network.
+ * @param isSatelliteConstrainedData True if the device satellite internet is bandwidth
+ * constrained.
+ *
+ * @hide
+ */
+ public TelephonyDisplayInfo(@NetworkType int networkType,
+ @OverrideNetworkType int overrideNetworkType,
+ boolean isRoaming, boolean isNtn, boolean isSatelliteConstrainedData) {
+ mNetworkType = networkType;
+ mOverrideNetworkType = overrideNetworkType;
+ mIsRoaming = isRoaming;
+ mIsNtn = isNtn;
+ mIsSatelliteConstrainedData = isSatelliteConstrainedData;
}
/** @hide */
@@ -131,6 +165,8 @@ public final class TelephonyDisplayInfo implements Parcelable {
mNetworkType = p.readInt();
mOverrideNetworkType = p.readInt();
mIsRoaming = p.readBoolean();
+ mIsNtn = p.readBoolean();
+ mIsSatelliteConstrainedData = p.readBoolean();
}
/**
@@ -170,11 +206,34 @@ public final class TelephonyDisplayInfo implements Parcelable {
return mIsRoaming;
}
+ /**
+ * Get whether the satellite internet is with bandwidth constrained capability set.
+ *
+ * @return {@code true} if satellite internet is connected with bandwidth constrained
+ * capability else {@code false}.
+ * @hide
+ */
+ public boolean isSatelliteConstrainedData() {
+ return mIsSatelliteConstrainedData;
+ }
+
+ /**
+ * Get whether the network is a non-terrestrial network.
+ *
+ * @return {@code true} if network is a non-terrestrial network else {@code false}.
+ * @hide
+ */
+ public boolean isNtn() {
+ return mIsNtn;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mNetworkType);
dest.writeInt(mOverrideNetworkType);
dest.writeBoolean(mIsRoaming);
+ dest.writeBoolean(mIsNtn);
+ dest.writeBoolean(mIsSatelliteConstrainedData);
}
public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR =
@@ -202,12 +261,15 @@ public final class TelephonyDisplayInfo implements Parcelable {
TelephonyDisplayInfo that = (TelephonyDisplayInfo) o;
return mNetworkType == that.mNetworkType
&& mOverrideNetworkType == that.mOverrideNetworkType
- && mIsRoaming == that.mIsRoaming;
+ && mIsRoaming == that.mIsRoaming
+ && mIsNtn == that.mIsNtn
+ && mIsSatelliteConstrainedData == that.mIsSatelliteConstrainedData;
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming);
+ return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming, mIsNtn,
+ mIsSatelliteConstrainedData);
}
/**
@@ -233,6 +295,8 @@ public final class TelephonyDisplayInfo implements Parcelable {
public String toString() {
return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+ ", overrideNetwork=" + overrideNetworkTypeToString(mOverrideNetworkType)
- + ", isRoaming=" + mIsRoaming + "}";
+ + ", isRoaming=" + mIsRoaming
+ + ", isNtn=" + mIsNtn
+ + ", isSatelliteConstrainedData=" + mIsSatelliteConstrainedData + "}";
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65a52daae99f..24fb8c5da2d7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1397,25 +1397,44 @@ public class TelephonyManager {
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming
* mode set to the radio default or to the user's preference if they've indicated one.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits
* connections on home networks.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_HOME = 0;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
* affiliated networks.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
* any network.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_ANY = 2;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
CDMA_ROAMING_MODE_RADIO_DEFAULT,
CDMA_ROAMING_MODE_HOME,
@@ -1802,12 +1821,17 @@ public class TelephonyManager {
* to indicate if the SIM combination in DSDS has limitation or compatible issue.
* e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
"android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef({
EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
@@ -1818,15 +1842,21 @@ public class TelephonyManager {
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate there's no SIM combination warning.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate two active SIMs are both CDMA hence there might be functional limitation.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
/**
@@ -1835,6 +1865,7 @@ public class TelephonyManager {
* e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
* name will be "operator1 & operator2".
*
+ * TODO(b/379356026): Deprecate if this is CDMA specific
* @hide
*/
public static final String EXTRA_SIM_COMBINATION_NAMES =
@@ -2414,13 +2445,17 @@ public class TelephonyManager {
* higher, then a SecurityException is thrown.</li>
* </ul>
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getMeid() {
+ if (Flags.cleanupCdma()) return null;
return getMeid(getSlotIndex());
}
@@ -2456,13 +2491,17 @@ public class TelephonyManager {
*
* @param slotIndex of which MEID is returned
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getMeid(int slotIndex) {
+ if (Flags.cleanupCdma()) return null;
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -2485,12 +2524,16 @@ public class TelephonyManager {
* Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
* available.
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode() {
+ if (Flags.cleanupCdma()) return null;
return getManufacturerCode(getSlotIndex());
}
@@ -2500,12 +2543,16 @@ public class TelephonyManager {
*
* @param slotIndex of which Type Allocation Code is returned
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode(int slotIndex) {
+ if (Flags.cleanupCdma()) return null;
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -2648,7 +2695,13 @@ public class TelephonyManager {
public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE;
/** Phone radio is GSM. */
public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM;
- /** Phone radio is CDMA. */
+ /**
+ * Phone radio is CDMA.
+ *
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
/** Phone is via SIP. */
public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
@@ -3070,13 +3123,33 @@ public class TelephonyManager {
public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
/** Current network is UMTS */
public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3.
- /** Current network is CDMA: Either IS95A or IS95B*/
+ /**
+ * Current network is CDMA: Either IS95A or IS95B
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4.
- /** Current network is EVDO revision 0*/
+ /**
+ * Current network is EVDO revision 0
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5.
- /** Current network is EVDO revision A*/
+ /**
+ * Current network is EVDO revision A
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6.
- /** Current network is 1xRTT*/
+ /**
+ * Current network is 1xRTT
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
/** Current network is HSDPA */
public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
@@ -3090,11 +3163,21 @@ public class TelephonyManager {
*/
@Deprecated
public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
- /** Current network is EVDO revision B*/
+ /**
+ * Current network is EVDO revision B
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
/** Current network is LTE */
public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
- /** Current network is eHRPD */
+ /**
+ * Current network is eHRPD
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
/** Current network is HSPA+ */
public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
@@ -6292,6 +6375,7 @@ public class TelephonyManager {
* @deprecated use {@link #getImsPrivateUserIdentity()}
*/
@UnsupportedAppUsage
+ @Deprecated
public String getIsimImpi() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
@@ -6381,6 +6465,7 @@ public class TelephonyManager {
* @deprecated use {@link #getImsPublicUserIdentities()}
*/
@UnsupportedAppUsage
+ @Deprecated
@Nullable
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String[] getIsimImpu() {
@@ -6761,9 +6846,13 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERI_"}, value = {
+ -1,
ERI_ON,
ERI_OFF,
ERI_FLASH
@@ -6773,24 +6862,37 @@ public class TelephonyManager {
/**
* ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_ON = 0;
/**
* ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_OFF = 1;
/**
* ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_FLASH = 2;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
+ -1,
ERI_ICON_MODE_NORMAL,
ERI_ICON_MODE_FLASH
})
@@ -6802,7 +6904,9 @@ public class TelephonyManager {
*
* Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
* @hide
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
public static final int ERI_ICON_MODE_NORMAL = 0;
/**
@@ -6811,7 +6915,9 @@ public class TelephonyManager {
*
* Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
* @hide
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
public static final int ERI_ICON_MODE_FLASH = 1;
/**
@@ -6819,24 +6925,31 @@ public class TelephonyManager {
* 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
* Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() {
+ if (Flags.cleanupCdma()) return -1;
return getCdmaEriIconIndex(getSubId());
}
/**
* Returns the CDMA ERI icon index to display for a subscription.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public @EriIconIndex int getCdmaEriIconIndex(int subId) {
+ if (Flags.cleanupCdma()) return -1;
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -6856,11 +6969,14 @@ public class TelephonyManager {
* 0 - ON
* 1 - FLASHING
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public @EriIconMode int getCdmaEriIconMode(int subId) {
+ if (Flags.cleanupCdma()) return -1;
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -6878,21 +6994,27 @@ public class TelephonyManager {
/**
* Returns the CDMA ERI text,
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getCdmaEriText() {
+ if (Flags.cleanupCdma()) return null;
return getCdmaEriText(getSubId());
}
/**
* Returns the CDMA ERI text, of a subscription
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@UnsupportedAppUsage
public String getCdmaEriText(int subId) {
+ if (Flags.cleanupCdma()) return null;
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -8166,10 +8288,13 @@ public class TelephonyManager {
* @param itemID the ID of the item to read.
* @return the NV item as a String, or null on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public String nvReadItem(int itemID) {
+ if (Flags.cleanupCdma()) return "";
try {
ITelephony telephony = getITelephony();
if (telephony != null)
@@ -8194,9 +8319,12 @@ public class TelephonyManager {
* @param itemValue the value to write, as a String.
* @return true on success; false on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public boolean nvWriteItem(int itemID, String itemValue) {
+ if (Flags.cleanupCdma()) return false;
try {
ITelephony telephony = getITelephony();
if (telephony != null)
@@ -8220,9 +8348,12 @@ public class TelephonyManager {
* @param preferredRoamingList byte array containing the new PRL.
* @return true on success; false on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
+ if (Flags.cleanupCdma()) return false;
try {
ITelephony telephony = getITelephony();
if (telephony != null)
@@ -8248,12 +8379,19 @@ public class TelephonyManager {
* {@link #resetRadioConfig()} for reset type 3 (b/116476729)
*
* @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
+ * @deprecated NV APIs are deprecated starting from Android U.
* @return true on success; false on any failure.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public boolean nvResetConfig(int resetType) {
+ if (Flags.cleanupCdma()) {
+ if (resetType != 1) { // 1: reload NV reset (reboot modem)
+ return false;
+ }
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -8283,14 +8421,20 @@ public class TelephonyManager {
*
* @return {@code true} on success; {@code false} on any failure.
*
+ * @deprecated NV APIs are deprecated starting from Android U.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean resetRadioConfig() {
+ if (Flags.cleanupCdma()) {
+ return false;
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -8319,6 +8463,7 @@ public class TelephonyManager {
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -8355,7 +8500,9 @@ public class TelephonyManager {
if (telephony == null) {
throw new IllegalStateException("telephony service is null.");
}
- telephony.rebootModem(getSlotIndex());
+ if (!telephony.rebootModem(getSlotIndex())) {
+ throw new RuntimeException("Couldn't reboot modem (it may be not supported)");
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "rebootRadio RemoteException", ex);
throw ex.rethrowAsRuntimeException();
@@ -9272,20 +9419,26 @@ public class TelephonyManager {
/**
* Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
/**
* Preferred network mode is CDMA only.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
/**
* Preferred network mode is EvDo only.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
/**
@@ -9296,8 +9449,10 @@ public class TelephonyManager {
/**
* Preferred network mode is LTE, CDMA and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
/**
@@ -9308,8 +9463,10 @@ public class TelephonyManager {
/**
* Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
@@ -9379,14 +9536,18 @@ public class TelephonyManager {
/**
* Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
/**
* Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
@@ -9404,8 +9565,10 @@ public class TelephonyManager {
/**
* Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
@@ -9418,8 +9581,10 @@ public class TelephonyManager {
/**
* Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
@@ -9458,8 +9623,10 @@ public class TelephonyManager {
/**
* Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
@@ -10533,24 +10700,32 @@ public class TelephonyManager {
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMdn() {
+ if (Flags.cleanupCdma()) return null;
return getCdmaMdn(getSubId());
}
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMdn(int subId) {
+ if (Flags.cleanupCdma()) return null;
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -10566,24 +10741,32 @@ public class TelephonyManager {
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMin() {
+ if (Flags.cleanupCdma()) return null;
return getCdmaMin(getSubId());
}
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMin(int subId) {
+ if (Flags.cleanupCdma()) return null;
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -11868,12 +12051,16 @@ public class TelephonyManager {
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @CdmaRoamingMode int getCdmaRoamingMode() {
+ if (Flags.cleanupCdma()) return CDMA_ROAMING_MODE_RADIO_DEFAULT;
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
try {
ITelephony telephony = getITelephony();
@@ -11912,12 +12099,16 @@ public class TelephonyManager {
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public void setCdmaRoamingMode(@CdmaRoamingMode int mode) {
+ if (Flags.cleanupCdma()) return;
if (getPhoneType() != PHONE_TYPE_CDMA) {
throw new IllegalStateException("Phone does not support CDMA.");
}
@@ -11935,7 +12126,10 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = {
CDMA_SUBSCRIPTION_UNKNOWN,
CDMA_SUBSCRIPTION_RUIM_SIM,
@@ -11946,22 +12140,31 @@ public class TelephonyManager {
/**
* Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
/**
* Used for CDMA subscription mode: RUIM/SIM (default)
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
/**
* Used for CDMA subscription mode: NV -> non-volatile memory
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_NV = 1;
@@ -11982,12 +12185,16 @@ public class TelephonyManager {
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @CdmaSubscription int getCdmaSubscriptionMode() {
+ if (Flags.cleanupCdma()) return CDMA_SUBSCRIPTION_UNKNOWN;
int mode = CDMA_SUBSCRIPTION_RUIM_SIM;
try {
ITelephony telephony = getITelephony();
@@ -12022,12 +12229,16 @@ public class TelephonyManager {
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public void setCdmaSubscriptionMode(@CdmaSubscription int mode) {
+ if (Flags.cleanupCdma()) return;
if (getPhoneType() != PHONE_TYPE_CDMA) {
throw new IllegalStateException("Phone does not support CDMA.");
}
@@ -13747,11 +13958,15 @@ public class TelephonyManager {
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaPrlVersion() {
+ if (Flags.cleanupCdma()) return null;
return getCdmaPrlVersion(getSubId());
}
@@ -13762,9 +13977,12 @@ public class TelephonyManager {
*
* @param subId the subscription ID that this request applies to.
* @return PRLVersion or null if error.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public String getCdmaPrlVersion(int subId) {
+ if (Flags.cleanupCdma()) return null;
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -13818,6 +14036,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
@@ -14928,7 +15147,10 @@ public class TelephonyManager {
public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
/**
* network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
/**
* network type bitmask indicating the support of radio tech 1xRTT.
@@ -14950,7 +15172,10 @@ public class TelephonyManager {
public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
/**
* network type bitmask indicating the support of radio tech EHRPD.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
/**
* network type bitmask indicating the support of radio tech HSUPA.
@@ -15047,7 +15272,10 @@ public class TelephonyManager {
| NETWORK_TYPE_BITMASK_LTE_CA
| NETWORK_TYPE_BITMASK_NR;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA
| NETWORK_TYPE_BITMASK_1xRTT
| NETWORK_TYPE_BITMASK_EVDO_0
diff --git a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
index 02429b5c2a2c..8fccf6505c0b 100644
--- a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
+++ b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
@@ -16,12 +16,15 @@
package android.telephony.cdma;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,18 +37,36 @@ import java.lang.annotation.RetentionPolicy;
* containing an array of these objects to update its list of cell broadcast service categories
* to display.
*
+ * @deprecated Legacy CDMA is unsupported.
* {@hide}
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
@SystemApi
public final class CdmaSmsCbProgramData implements Parcelable {
- /** Delete the specified service category from the list of enabled categories. */
+ /**
+ * Delete the specified service category from the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_DELETE_CATEGORY = 0;
- /** Add the specified service category to the list of enabled categories. */
+ /**
+ * Add the specified service category to the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_ADD_CATEGORY = 1;
- /** Clear all service categories from the list of enabled categories. */
+ /**
+ * Clear all service categories from the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_CLEAR_CATEGORIES = 2;
/** @hide */
@@ -59,23 +80,53 @@ public final class CdmaSmsCbProgramData implements Parcelable {
public @interface Operation {}
// CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
- /** Indicates a presidential-level alert */
+ /**
+ * Indicates a presidential-level alert
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 0x1000;
- /** Indicates an extreme threat to life and property */
+ /**
+ * Indicates an extreme threat to life and property
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_EXTREME_THREAT = 0x1001;
- /** Indicates an severe threat to life and property */
+ /**
+ * Indicates an severe threat to life and property
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_SEVERE_THREAT = 0x1002;
- /** Indicates an AMBER child abduction emergency */
+ /**
+ * Indicates an AMBER child abduction emergency
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
- /** Indicates a CMAS test message */
+ /**
+ * Indicates a CMAS test message
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_TEST_MESSAGE = 0x1004;
- /** The last reserved value of a CMAS service category according to 3GPP C.R1001 table
- * 9.3.3-1. */
+ /**
+ * The last reserved value of a CMAS service category according to 3GPP C.R1001 table
+ * 9.3.3-1.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 0x10ff;
/** @hide */
@@ -177,7 +228,10 @@ public final class CdmaSmsCbProgramData implements Parcelable {
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written (ignored).
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mOperation);
@@ -192,7 +246,10 @@ public final class CdmaSmsCbProgramData implements Parcelable {
* Returns the service category operation, e.g. {@link #OPERATION_ADD_CATEGORY}.
*
* @return the service category operation
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public @Operation int getOperation() {
return mOperation;
}
@@ -203,7 +260,10 @@ public final class CdmaSmsCbProgramData implements Parcelable {
* 0x10FF are supported.
*
* @return a 16-bit CDMA service category value
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public @Category int getCategory() {
return mCategory;
}
@@ -255,7 +315,11 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Describe the kinds of special objects contained in the marshalled representation.
* @return a bitmask indicating this Parcelable contains no special objects
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public int describeContents() {
return 0;
@@ -263,7 +327,11 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Creator for unparcelling objects.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@NonNull
public static final Parcelable.Creator<CdmaSmsCbProgramData>
CREATOR = new Parcelable.Creator<CdmaSmsCbProgramData>() {
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 8925a9e82942..76f83ee49dcd 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -18,6 +18,7 @@ package android.telephony.ims;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,8 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -226,8 +229,11 @@ public class RcsUceAdapter {
/**
* A capability update has been requested due to moving to eHRPD.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
diff --git a/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
index 5e276aa49b05..0a1eedf097a4 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
@@ -18,6 +18,7 @@ package android.telephony.satellite;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import com.android.internal.telephony.flags.Flags;
@@ -26,13 +27,14 @@ import com.android.internal.telephony.flags.Flags;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteDisallowedReasonsCallback {
/**
* Called when disallowed reason of satellite has changed.
* @param disallowedReasons Integer array of disallowed reasons.
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
- void onSatelliteDisallowedReasonsChanged(@NonNull int[] disallowedReasons);
+ void onSatelliteDisallowedReasonsChanged(
+ @NonNull @SatelliteManager.SatelliteDisallowedReason int[] disallowedReasons);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 1025c1506d4a..cf807a295a2d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -560,6 +560,8 @@ public final class SatelliteManager {
* There is no valid satellite subscription selected.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION = 30;
/** @hide */
@@ -2393,8 +2395,9 @@ public final class SatelliteManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void requestSatelliteAccessConfigurationForCurrentLocation(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteAccessConfiguration, SatelliteException> callback) {
@@ -2507,7 +2510,7 @@ public final class SatelliteManager {
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
- * will return the time after which the satellite will be visible.
+ * will return the selected NB IOT satellite subscription ID.
* If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
* will return a {@link SatelliteException} with the {@link SatelliteResult}.
*
@@ -2515,6 +2518,8 @@ public final class SatelliteManager {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
public void requestSelectedNbIotSatelliteSubscriptionId(
@NonNull @CallbackExecutor Executor executor,
@@ -2569,11 +2574,15 @@ public final class SatelliteManager {
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle the selected satellite subscription changed event.
*
+ * @return The {@link SatelliteResult} result of the operation.
+ *
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult public int registerForSelectedNbIotSatelliteSubscriptionChanged(
@NonNull @CallbackExecutor Executor executor,
@@ -2619,6 +2628,8 @@ public final class SatelliteManager {
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
public void unregisterForSelectedNbIotSatelliteSubscriptionChanged(
@NonNull SelectedNbIotSatelliteSubscriptionCallback callback) {
@@ -2897,27 +2908,22 @@ public final class SatelliteManager {
/**
* Returns list of disallowed reasons of satellite.
*
- * @return list of disallowed reasons of satellite.
+ * @return Integer array of disallowed reasons.
*
* @throws SecurityException if caller doesn't have required permission.
* @throws IllegalStateException if Telephony process isn't available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteDisallowedReason
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@NonNull
- public List<Integer> getSatelliteDisallowedReasons() {
+ public int[] getSatelliteDisallowedReasons() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int[] receivedArray = telephony.getSatelliteDisallowedReasons();
- if (receivedArray.length == 0) {
- logd("receivedArray is empty, create empty list");
- return new ArrayList<>();
- } else {
- return Arrays.stream(receivedArray).boxed().collect(Collectors.toList());
- }
+ return telephony.getSatelliteDisallowedReasons();
} else {
throw new IllegalStateException("Telephony service is null.");
}
@@ -2925,7 +2931,7 @@ public final class SatelliteManager {
loge("getSatelliteDisallowedReasons() RemoteException: " + ex);
ex.rethrowAsRuntimeException();
}
- return new ArrayList<>();
+ return new int[0];
}
/**
@@ -2938,8 +2944,9 @@ public final class SatelliteManager {
* @throws IllegalStateException if Telephony process is not available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void registerForSatelliteDisallowedReasonsChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteDisallowedReasonsCallback callback) {
@@ -2981,8 +2988,9 @@ public final class SatelliteManager {
* @throws IllegalStateException if Telephony process is not available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForSatelliteDisallowedReasonsChanged(
@NonNull SatelliteDisallowedReasonsCallback callback) {
Objects.requireNonNull(callback);
@@ -3295,7 +3303,7 @@ public final class SatelliteManager {
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle the satellite supoprted state changed event.
*
- * @return The result of the operation.
+ * @return The {@link SatelliteResult} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -3377,7 +3385,7 @@ public final class SatelliteManager {
*
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle satellite communication allowed state changed event.
- * @return The result of the operation.
+ * @return The {@link SatelliteResult} result of the operation.
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
@@ -3606,8 +3614,8 @@ public final class SatelliteManager {
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
- * will return display name of the satellite feature in string format. Defaults
- * to satellite. If the request is not successful,
+ * will return display name of the satellite feature in string format. Default
+ * display name is "Satellite". If the request is not successful,
* {@link OutcomeReceiver#onError(Throwable)} will return an error with
* a SatelliteException.
*
@@ -3618,7 +3626,7 @@ public final class SatelliteManager {
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
public void requestSatelliteDisplayName(
@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<String, SatelliteException> callback) {
+ @NonNull OutcomeReceiver<CharSequence, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -3630,7 +3638,7 @@ public final class SatelliteManager {
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SATELLITE_RESULT_SUCCESS) {
if (resultData.containsKey(KEY_SATELLITE_DISPLAY_NAME)) {
- String satelliteDisplayName =
+ CharSequence satelliteDisplayName =
resultData.getString(KEY_SATELLITE_DISPLAY_NAME);
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onResult(satelliteDisplayName)));
diff --git a/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
index d8965547a20e..76caef3c9c7c 100644
--- a/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
+++ b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
@@ -16,11 +16,18 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
/**
* A callback class for selected satellite subscription changed events.
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SelectedNbIotSatelliteSubscriptionCallback {
/**
* Called when the selected satellite subscription has changed.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index eaeed2a0a9fa..b1a5b1bebc59 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -17,6 +17,7 @@
package android.telephony.satellite.stub;
import android.annotation.NonNull;
+import android.hardware.radio.network.IRadioNetwork;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.IBooleanConsumer;
@@ -586,7 +587,11 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
* SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
* SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ *
+ * @deprecated Use
+ * {@link IRadioNetwork#setSatellitePlmn(int, int, String[], String[])}.
*/
+ @Deprecated
public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
@NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
@NonNull IIntegerConsumer resultCallback) {
@@ -608,7 +613,11 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
* SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
* SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ *
+ * @deprecated Use
+ * {@link IRadioNetwork#setSatelliteEnabledForCarrier(int, int, boolean)}.
*/
+ @Deprecated
public void setSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
@NonNull boolean satelliteEnabled, @NonNull IIntegerConsumer callback) {
// stub implementation
@@ -629,7 +638,11 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
* SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
* SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ *
+ * @deprecated Use
+ * {@link IRadioNetwork#isSatelliteEnabledForCarrier(int, int)}.
*/
+ @Deprecated
public void requestIsSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
// stub implementation
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index da7669fd81ad..74d9204e9c84 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -416,6 +416,8 @@ interface ITelephony {
* Returns the CDMA ERI icon index to display
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconIndex(String callingPackage, String callingFeatureId);
@@ -424,6 +426,8 @@ interface ITelephony {
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage,
String callingFeatureId);
@@ -434,6 +438,8 @@ interface ITelephony {
* 1 - FLASHING
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconMode(String callingPackage, String callingFeatureId);
@@ -444,6 +450,8 @@ interface ITelephony {
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconModeForSubscriber(int subId, String callingPackage,
String callingFeatureId);
@@ -452,6 +460,8 @@ interface ITelephony {
* Returns the CDMA ERI text,
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaEriText(String callingPackage, String callingFeatureId);
@@ -460,6 +470,8 @@ interface ITelephony {
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaEriTextForSubscriber(int subId, String callingPackage, String callingFeatureId);
@@ -779,6 +791,8 @@ interface ITelephony {
*
* @param itemID the ID of the item to read.
* @return the NV item as a String, or null on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String nvReadItem(int itemID);
@@ -789,6 +803,8 @@ interface ITelephony {
* @param itemID the ID of the item to read.
* @param itemValue the value to write, as a String.
* @return true on success; false on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean nvWriteItem(int itemID, String itemValue);
@@ -798,6 +814,8 @@ interface ITelephony {
*
* @param preferredRoamingList byte array containing the new PRL.
* @return true on success; false on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean nvWriteCdmaPrl(in byte[] preferredRoamingList);
@@ -811,6 +829,8 @@ interface ITelephony {
*
* @param slotIndex - device slot.
* @return {@code true} on success; {@code false} on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean resetModemConfig(int slotIndex);
@@ -1041,12 +1061,16 @@ interface ITelephony {
/**
* Return MDN string for CDMA phone.
* @param subId user preferred subId.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaMdn(int subId);
/**
* Return MIN string for CDMA phone.
* @param subId user preferred subId.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaMin(int subId);
@@ -1495,13 +1519,14 @@ interface ITelephony {
String getEsn(int subId);
/**
- * Return the Preferred Roaming List Version
- *
- * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
- * @param subId the subscription ID that this request applies to.
- * @return PRLVersion or null if error.
- * @hide
- */
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ * @param subId the subscription ID that this request applies to.
+ * @return PRLVersion or null if error.
+ * @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
String getCdmaPrlVersion(int subId);
/**
@@ -1804,6 +1829,8 @@ interface ITelephony {
*
* @param the subscription id.
* @return the roaming mode for CDMA phone.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaRoamingMode(int subId);
@@ -1814,6 +1841,8 @@ interface ITelephony {
* @param subId the subscription id.
* @param mode the roaming mode should be set.
* @return {@code true} if successed.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean setCdmaRoamingMode(int subId, int mode);
@@ -1822,6 +1851,8 @@ interface ITelephony {
*
* @param the subscription id.
* @return the subscription mode for CDMA phone.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaSubscriptionMode(int subId);
@@ -1832,6 +1863,8 @@ interface ITelephony {
* @param subId the subscription id.
* @param mode the subscription mode should be set.
* @return {@code true} if successed.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean setCdmaSubscriptionMode(int subId, int mode);
diff --git a/tests/AppJankTest/Android.bp b/tests/AppJankTest/Android.bp
index acf8dc9aca47..c3cda6a41cbb 100644
--- a/tests/AppJankTest/Android.bp
+++ b/tests/AppJankTest/Android.bp
@@ -30,6 +30,7 @@ android_test {
"androidx.test.core",
"platform-test-annotations",
"flag-junit",
+ "androidx.test.uiautomator_uiautomator",
],
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/tests/AppJankTest/AndroidManifest.xml b/tests/AppJankTest/AndroidManifest.xml
index abed1798c47c..861a79c6f0ed 100644
--- a/tests/AppJankTest/AndroidManifest.xml
+++ b/tests/AppJankTest/AndroidManifest.xml
@@ -19,22 +19,31 @@
package="android.app.jank.tests">
<application android:appCategory="news">
- <uses-library android:name="android.test.runner" />
+ <activity android:name=".JankTrackerActivity"
+ android:exported="true"
+ android:label="JankTrackerActivity"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
+ </intent-filter>
+ </activity>
<activity android:name=".EmptyActivity"
- android:label="EmptyActivity"
- android:exported="true">
+ android:exported="true"
+ android:label="EmptyActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
</intent-filter>
</activity>
+ <uses-library android:name="android.test.runner"/>
</application>
<!-- self-instrumenting test package. -->
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.app.jank.tests"
- android:label="Core tests of App Jank Tracking">
+ android:label="Core tests of App Jank Tracking"
+ android:targetPackage="android.app.jank.tests">
</instrumentation>
</manifest> \ No newline at end of file
diff --git a/tests/AppJankTest/res/layout/jank_tracker_activity_layout.xml b/tests/AppJankTest/res/layout/jank_tracker_activity_layout.xml
new file mode 100644
index 000000000000..65def7f2d5b6
--- /dev/null
+++ b/tests/AppJankTest/res/layout/jank_tracker_activity_layout.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <LinearLayout
+ android:id="@+id/linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <EditText
+ android:id="@+id/edit_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textEnableTextConversionSuggestions"
+ android:text="Edit Text"/>
+ <TextView android:id="@+id/text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Text View"
+ />
+ <android.app.jank.tests.TestWidget
+ android:id="@+id/jank_tracker_widget"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
new file mode 100644
index 000000000000..34f0c191ecf5
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.app.jank.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.jank.AppJankStats;
+import android.app.jank.Flags;
+import android.app.jank.JankDataProcessor;
+import android.app.jank.JankTracker;
+import android.app.jank.StateTracker;
+import android.content.Intent;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.widget.EditText;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This file contains tests that verify the proper functionality of the Jank Tracking feature.
+ * All tests should obtain references to necessary objects through View type interfaces, rather
+ * than direct instantiation. When operating outside of a testing environment, the expected
+ * behavior is to retrieve the necessary objects using View type interfaces. This approach ensures
+ * that calls are correctly routed down to the activity level. Any modifications to the call
+ * routing should result in test failures, which might happen with direct instantiations.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IntegrationTests {
+ public static final int WAIT_FOR_TIMEOUT_MS = 5000;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private Activity mEmptyActivity;
+
+ public UiDevice mDevice;
+ private Instrumentation mInstrumentation;
+ private ActivityTestRule<JankTrackerActivity> mJankTrackerActivityRule =
+ new ActivityTestRule<>(
+ JankTrackerActivity.class,
+ false,
+ false);
+
+ private ActivityTestRule<EmptyActivity> mEmptyActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class, false , true);
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mDevice = UiDevice.getInstance(mInstrumentation);
+ }
+
+
+ /**
+ * Get a JankTracker object from a view and confirm it's not null.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void getJankTacker_confirmNotNull() {
+ Activity jankTrackerActivity = mJankTrackerActivityRule.launchActivity(null);
+ EditText editText = jankTrackerActivity.findViewById(R.id.edit_text);
+
+ mDevice.wait(Until.findObject(By.text("Edit Text")), WAIT_FOR_TIMEOUT_MS);
+
+ JankTracker jankTracker = editText.getJankTracker();
+ assertTrue(jankTracker != null);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void reportJankStats_confirmPendingStatsIncreases() {
+ Activity jankTrackerActivity = mJankTrackerActivityRule.launchActivity(null);
+ EditText editText = jankTrackerActivity.findViewById(R.id.edit_text);
+ JankTracker jankTracker = editText.getJankTracker();
+
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ jankTracker.getPendingJankStats();
+ assertEquals(0, pendingStats.size());
+
+ editText.reportAppJankStats(JankUtils.getAppJankStats());
+
+ // reportAppJankStats performs the work on a background thread, check periodically to see
+ // if the work is complete.
+ for (int i = 0; i < 10; i++) {
+ try {
+ Thread.sleep(100);
+ if (jankTracker.getPendingJankStats().size() > 0) {
+ break;
+ }
+ } catch (InterruptedException exception) {
+ //do nothing and continue
+ }
+ }
+
+ pendingStats = jankTracker.getPendingJankStats();
+
+ assertEquals(1, pendingStats.size());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void simulateWidgetStateChanges_confirmStateChangesAreTracked() {
+ JankTrackerActivity jankTrackerActivity =
+ mJankTrackerActivityRule.launchActivity(null);
+ TestWidget testWidget = jankTrackerActivity.findViewById(R.id.jank_tracker_widget);
+ JankTracker jankTracker = testWidget.getJankTracker();
+ jankTracker.forceListenerRegistration();
+
+ ArrayList<StateTracker.StateData> uiStates = new ArrayList<>();
+ // Get the current UI states, at this point only the activity name should be in the UI
+ // states list.
+ jankTracker.getAllUiStates(uiStates);
+
+ assertEquals(1, uiStates.size());
+
+ // This should add a UI state to be tracked.
+ testWidget.simulateAnimationStarting();
+ uiStates.clear();
+ jankTracker.getAllUiStates(uiStates);
+
+ assertEquals(2, uiStates.size());
+
+ // Stop the animation
+ testWidget.simulateAnimationEnding();
+ uiStates.clear();
+ jankTracker.getAllUiStates(uiStates);
+
+ assertEquals(2, uiStates.size());
+
+ // Confirm the Animation state has a VsyncIdEnd that is not default, indicating the end
+ // of that state.
+ for (int i = 0; i < uiStates.size(); i++) {
+ StateTracker.StateData stateData = uiStates.get(i);
+ if (stateData.mWidgetCategory.equals(AppJankStats.ANIMATION)) {
+ assertNotEquals(Long.MAX_VALUE, stateData.mVsyncIdEnd);
+ }
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTrackingPaused_whenActivityNoLongerVisible() {
+ JankTrackerActivity jankTrackerActivity =
+ mJankTrackerActivityRule.launchActivity(null);
+ TestWidget testWidget = jankTrackerActivity.findViewById(R.id.jank_tracker_widget);
+ JankTracker jankTracker = testWidget.getJankTracker();
+ jankTracker.forceListenerRegistration();
+
+ assertTrue(jankTracker.shouldTrack());
+
+ // Send jankTrackerActivity to the background
+ mDevice.pressHome();
+ mDevice.waitForIdle(WAIT_FOR_TIMEOUT_MS);
+
+ assertFalse(jankTracker.shouldTrack());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTrackingResumed_whenActivityBecomesVisibleAgain() {
+ mEmptyActivityRule.launchActivity(null);
+ mEmptyActivity = mEmptyActivityRule.getActivity();
+ JankTrackerActivity jankTrackerActivity =
+ mJankTrackerActivityRule.launchActivity(null);
+ TestWidget testWidget = jankTrackerActivity.findViewById(R.id.jank_tracker_widget);
+ JankTracker jankTracker = testWidget.getJankTracker();
+ jankTracker.forceListenerRegistration();
+
+ // Send jankTrackerActivity to the background
+ mDevice.pressHome();
+ mDevice.waitForIdle(WAIT_FOR_TIMEOUT_MS);
+
+ assertFalse(jankTracker.shouldTrack());
+
+ Intent resumeJankTracker = new Intent(mInstrumentation.getContext(),
+ JankTrackerActivity.class);
+ resumeJankTracker.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ mEmptyActivity.startActivity(resumeJankTracker);
+ mDevice.wait(Until.findObject(By.text("Edit Text")), WAIT_FOR_TIMEOUT_MS);
+
+ assertTrue(jankTracker.shouldTrack());
+ }
+}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
index 4d495adf727b..30c568be7716 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
@@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals;
import android.app.jank.AppJankStats;
import android.app.jank.Flags;
-import android.app.jank.FrameOverrunHistogram;
import android.app.jank.JankDataProcessor;
import android.app.jank.StateTracker;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -165,7 +164,7 @@ public class JankDataProcessorTest {
assertEquals(pendingStats.size(), 0);
- AppJankStats jankStats = getAppJankStats();
+ AppJankStats jankStats = JankUtils.getAppJankStats();
mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
pendingStats = mJankDataProcessor.getPendingJankStats();
@@ -182,14 +181,14 @@ public class JankDataProcessorTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public void mergeAppJankStats_confirmStatsWithMatchingStatesAreCombinedIntoOnePendingStat() {
- AppJankStats jankStats = getAppJankStats();
+ AppJankStats jankStats = JankUtils.getAppJankStats();
mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
mJankDataProcessor.getPendingJankStats();
assertEquals(pendingStats.size(), 1);
- AppJankStats secondJankStat = getAppJankStats();
+ AppJankStats secondJankStat = JankUtils.getAppJankStats();
mJankDataProcessor.mergeJankStats(secondJankStat, sActivityName);
pendingStats = mJankDataProcessor.getPendingJankStats();
@@ -200,7 +199,7 @@ public class JankDataProcessorTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public void mergeAppJankStats_whenStatsWithMatchingStatesMerge_confirmFrameCountsAdded() {
- AppJankStats jankStats = getAppJankStats();
+ AppJankStats jankStats = JankUtils.getAppJankStats();
mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
@@ -345,27 +344,4 @@ public class JankDataProcessorTest {
return mockData;
}
-
- private AppJankStats getAppJankStats() {
- AppJankStats jankStats = new AppJankStats(
- /*App Uid*/APP_ID,
- /*Widget Id*/"test widget id",
- /*Widget Category*/AppJankStats.SCROLL,
- /*Widget State*/AppJankStats.SCROLLING,
- /*Total Frames*/100,
- /*Janky Frames*/25,
- getOverrunHistogram()
- );
- return jankStats;
- }
-
- private FrameOverrunHistogram getOverrunHistogram() {
- FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
- overrunHistogram.addFrameOverrunMillis(-2);
- overrunHistogram.addFrameOverrunMillis(1);
- overrunHistogram.addFrameOverrunMillis(5);
- overrunHistogram.addFrameOverrunMillis(25);
- return overrunHistogram;
- }
-
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerActivity.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerActivity.java
new file mode 100644
index 000000000000..80ab6ad3e587
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+
+public class JankTrackerActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.jank_tracker_activity_layout);
+ }
+}
+
+
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
new file mode 100644
index 000000000000..0b4d97ed20d6
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.app.jank.tests;
+
+import android.app.jank.AppJankStats;
+import android.app.jank.FrameOverrunHistogram;
+
+public class JankUtils {
+ private static final int APP_ID = 25;
+
+ /**
+ * Returns a mock AppJankStats object to be used in tests.
+ */
+ public static AppJankStats getAppJankStats() {
+ AppJankStats jankStats = new AppJankStats(
+ /*App Uid*/APP_ID,
+ /*Widget Id*/"test widget id",
+ /*Widget Category*/AppJankStats.SCROLL,
+ /*Widget State*/AppJankStats.SCROLLING,
+ /*Total Frames*/100,
+ /*Janky Frames*/25,
+ getOverrunHistogram()
+ );
+ return jankStats;
+ }
+
+ /**
+ * Returns a mock histogram to be used with an AppJankStats object.
+ */
+ public static FrameOverrunHistogram getOverrunHistogram() {
+ FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
+ overrunHistogram.addFrameOverrunMillis(-2);
+ overrunHistogram.addFrameOverrunMillis(1);
+ overrunHistogram.addFrameOverrunMillis(5);
+ overrunHistogram.addFrameOverrunMillis(25);
+ return overrunHistogram;
+ }
+}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
new file mode 100644
index 000000000000..5fff46038ead
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
@@ -0,0 +1,69 @@
+/*
+ * 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 android.app.jank.tests;
+
+import android.app.jank.AppJankStats;
+import android.app.jank.JankTracker;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class TestWidget extends View {
+
+ private JankTracker mJankTracker;
+
+ /**
+ * Create JankTrackerView
+ */
+ public TestWidget(Context context) {
+ super(context);
+ }
+
+ /**
+ * Create JankTrackerView, needed by system when inflating views defined in a layout file.
+ */
+ public TestWidget(Context context, AttributeSet attributeSet) {
+ super(context, attributeSet);
+ }
+
+ /**
+ * Mock starting an animation.
+ */
+ public void simulateAnimationStarting() {
+ if (jankTrackerCreated()) {
+ mJankTracker.addUiState(AppJankStats.ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ }
+ }
+
+ /**
+ * Mock ending an animation.
+ */
+ public void simulateAnimationEnding() {
+ if (jankTrackerCreated()) {
+ mJankTracker.removeUiState(AppJankStats.ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ }
+ }
+
+ private boolean jankTrackerCreated() {
+ if (mJankTracker == null) {
+ mJankTracker = getJankTracker();
+ }
+ return mJankTracker != null;
+ }
+}
diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml
index a4c898d8159a..68ec1233cdd7 100644
--- a/tests/Input/res/xml/bookmarks.xml
+++ b/tests/Input/res/xml/bookmarks.xml
@@ -23,7 +23,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -31,21 +31,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
@@ -57,7 +49,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
shift="true" />
<bookmark
@@ -65,4 +57,4 @@
class="com.test.BookmarkTest"
androidprv:keycode="KEYCODE_J"
shift="true" />
-</bookmarks> \ No newline at end of file
+</bookmarks>
diff --git a/tests/Input/res/xml/bookmarks_legacy.xml b/tests/Input/res/xml/bookmarks_legacy.xml
index 8bacf490ad9e..78cc48b19416 100644
--- a/tests/Input/res/xml/bookmarks_legacy.xml
+++ b/tests/Input/res/xml/bookmarks_legacy.xml
@@ -22,23 +22,17 @@
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c" />
+ shortcut="p" />
<bookmark
category="android.intent.category.APP_EMAIL"
shortcut="e" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="k" />
+ shortcut="c" />
<bookmark
category="android.intent.category.APP_MAPS"
shortcut="m" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- shortcut="p" />
- <bookmark
- role="android.app.role.SMS"
- shortcut="s" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
shortcut="u" />
@@ -49,7 +43,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c"
+ shortcut="p"
shift="true" />
<bookmark
@@ -57,4 +51,4 @@
class="com.test.BookmarkTest"
shortcut="j"
shift="true" />
-</bookmarks> \ No newline at end of file
+</bookmarks>
diff --git a/tests/Input/src/com/android/server/input/InputDataStoreTests.kt b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
new file mode 100644
index 000000000000..78c828bafd8f
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
@@ -0,0 +1,504 @@
+/*
+ * Copyright 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.server.input
+
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.hardware.input.AppLaunchData
+import android.hardware.input.InputGestureData
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.Presubmit
+import android.util.AtomicFile
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.nio.charset.StandardCharsets
+import kotlin.test.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito
+
+/**
+ * Tests for {@link InputDataStore}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDataStoreTests
+ */
+@Presubmit
+class InputDataStoreTests {
+
+ companion object {
+ const val USER_ID = 1
+ }
+
+ private lateinit var context: Context
+ private lateinit var inputDataStore: InputDataStore
+ private lateinit var tempFile: File
+
+ @Before
+ fun setup() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ setupInputDataStore()
+ }
+
+ private fun setupInputDataStore() {
+ tempFile = File.createTempFile("input_gestures", ".xml")
+ inputDataStore = InputDataStore(object : InputDataStore.FileInjector("input_gestures") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
+
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
+
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
+
+ override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
+ }
+ })
+ }
+
+ private fun getPrintableXml(inputGestures: List<InputGestureData>): String {
+ val outputStream = ByteArrayOutputStream()
+ inputDataStore.writeInputGestureXml(outputStream, true, inputGestures)
+ return outputStream.toString(StandardCharsets.UTF_8).trimIndent()
+ }
+
+ @Test
+ fun saveToDiskKeyGesturesOnly() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_H,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskTouchpadGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskAppLaunchGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER))
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForComponent(
+ "com.test",
+ "com.test.BookmarkTest"
+ )
+ )
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskCombinedGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_9,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
+ .build(),
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun validXmlParse() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun missingTriggerData() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidKeycode() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9999999" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTriggerName() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <invalid_trigger_name touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTouchpadGestureType() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="9999" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun emptyInputGestureList() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ listOf(),
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTag() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <invalid_tag_name>
+ </invalid_tag_name>
+ </root>""".trimIndent()
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ listOf(),
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+} \ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 43844f6514e8..aea75d8e7c85 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -28,10 +28,11 @@ import android.hardware.display.DisplayViewport
import android.hardware.display.VirtualDisplay
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
+import android.hardware.input.InputSettings
+import android.hardware.input.KeyGestureEvent
import android.os.InputEventInjectionSync
import android.os.SystemClock
import android.os.test.TestLooper
-import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
@@ -99,6 +100,7 @@ class InputManagerServiceTests {
.mockStatic(LocalServices::class.java)
.mockStatic(PermissionChecker::class.java)
.mockStatic(KeyCharacterMap::class.java)
+ .mockStatic(InputSettings::class.java)
.build()!!
@get:Rule
@@ -481,32 +483,102 @@ class InputManagerServiceTests {
}
@Test
- @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
fun handleKeyGestures_keyboardBacklight() {
- service.systemRunning()
-
- val backlightDownEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN)
- service.interceptKeyBeforeDispatching(null, backlightDownEvent, /* policyFlags = */0)
+ val backlightDownEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(backlightDownEvent)
verify(kbdController).decrementKeyboardBacklight(anyInt())
- val backlightUpEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP)
- service.interceptKeyBeforeDispatching(null, backlightUpEvent, /* policyFlags = */0)
+ val backlightUpEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(backlightUpEvent)
verify(kbdController).incrementKeyboardBacklight(anyInt())
}
@Test
- @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
- fun handleKeyGestures_toggleCapsLock() {
- service.systemRunning()
+ fun handleKeyGestures_a11yBounceKeysShortcut() {
+ ExtendedMockito.doReturn(true).`when` {
+ InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+ }
+ val toggleBounceKeysEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(toggleBounceKeysEvent)
+ ExtendedMockito.verify {
+ InputSettings.setAccessibilityBounceKeysThreshold(
+ any(),
+ eq(InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS)
+ )
+ }
+ }
- val metaDownEvent = createKeyEvent(KeyEvent.KEYCODE_META_LEFT)
- service.interceptKeyBeforeDispatching(null, metaDownEvent, /* policyFlags = */0)
- val altDownEvent =
- createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_DOWN)
- service.interceptKeyBeforeDispatching(null, altDownEvent, /* policyFlags = */0)
- val altUpEvent =
- createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_UP)
- service.interceptKeyBeforeDispatching(null, altUpEvent, /* policyFlags = */0)
+ @Test
+ fun handleKeyGestures_a11yMouseKeysShortcut() {
+ ExtendedMockito.doReturn(true).`when` {
+ InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+ }
+ val toggleMouseKeysEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(toggleMouseKeysEvent)
+ ExtendedMockito.verify {
+ InputSettings.setAccessibilityMouseKeysEnabled(any(), eq(true))
+ }
+ }
+
+ @Test
+ fun handleKeyGestures_a11yStickyKeysShortcut() {
+ ExtendedMockito.doReturn(true).`when` {
+ InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+ }
+ val toggleStickyKeysEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(toggleStickyKeysEvent)
+ ExtendedMockito.verify {
+ InputSettings.setAccessibilityStickyKeysEnabled(any(), eq(true))
+ }
+ }
+
+ @Test
+ fun handleKeyGestures_a11ySlowKeysShortcut() {
+ ExtendedMockito.doReturn(true).`when` {
+ InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+ }
+ val toggleSlowKeysEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(toggleSlowKeysEvent)
+ ExtendedMockito.verify {
+ InputSettings.setAccessibilitySlowKeysThreshold(
+ any(),
+ eq(InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS)
+ )
+ }
+ }
+
+ @Test
+ fun handleKeyGestures_toggleCapsLock() {
+ val toggleCapsLockEvent =
+ KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ service.handleKeyGestureEvent(toggleCapsLockEvent)
verify(native).toggleCapsLock(anyInt())
}
@@ -545,25 +617,6 @@ class InputManagerServiceTests {
)
whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
}
-
- private fun createKeyEvent(
- keycode: Int,
- modifierState: Int = 0,
- action: Int = KeyEvent.ACTION_DOWN
- ): KeyEvent {
- return KeyEvent(
- /* downTime = */0,
- /* eventTime = */0,
- action,
- keycode,
- /* repeat = */0,
- modifierState,
- KeyCharacterMap.VIRTUAL_KEYBOARD,
- /* scancode = */0,
- /* flags = */0,
- InputDevice.SOURCE_KEYBOARD
- )
- }
}
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 36a89f95aa6f..fafb0e0f75c8 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -40,6 +40,7 @@ import android.os.test.TestLooper
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
+import android.util.AtomicFile
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyEvent
@@ -50,6 +51,9 @@ import com.android.internal.R
import com.android.internal.annotations.Keep
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
import junitparams.JUnitParamsRunner
import junitparams.Parameters
import org.junit.After
@@ -123,6 +127,8 @@ class KeyGestureControllerTests {
private lateinit var keyGestureController: KeyGestureController
private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var testLooper: TestLooper
+ private lateinit var tempFile: File
+ private lateinit var inputDataStore: InputDataStore
private var events = mutableListOf<KeyGestureEvent>()
@Before
@@ -133,6 +139,31 @@ class KeyGestureControllerTests {
setupBehaviors()
testLooper = TestLooper()
currentPid = Process.myPid()
+ tempFile = File.createTempFile("input_gestures", ".xml")
+ inputDataStore =
+ InputDataStore(object : InputDataStore.FileInjector("input_gestures.xml") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
+
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
+
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
+
+ override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
+ }
+
+ override fun getAtomicFileForUserId(userId: Int): AtomicFile {
+ return atomicFile
+ }
+ })
}
@After
@@ -174,10 +205,12 @@ class KeyGestureControllerTests {
}
private fun setupKeyGestureController() {
- keyGestureController = KeyGestureController(context, testLooper.looper)
+ keyGestureController =
+ KeyGestureController(context, testLooper.looper, inputDataStore)
Mockito.`when`(iInputManager.getAppLaunchBookmarks())
.thenReturn(keyGestureController.appLaunchBookmarks)
keyGestureController.systemRunning()
+ testLooper.dispatchAll()
}
private fun notifyHomeGestureCompleted() {
@@ -542,9 +575,9 @@ class KeyGestureControllerTests {
),
TestData(
"META + C -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -560,9 +593,9 @@ class KeyGestureControllerTests {
),
TestData(
"META + K -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -577,24 +610,6 @@ class KeyGestureControllerTests {
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
- "META + P -> Launch Default Music",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
- ),
- TestData(
- "META + S -> Launch Default SMS",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_S),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
- ),
- TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -839,10 +854,10 @@ class KeyGestureControllerTests {
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + C -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ "META + P -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -857,10 +872,10 @@ class KeyGestureControllerTests {
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
),
TestData(
- "META + K -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ "META + C -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -875,24 +890,6 @@ class KeyGestureControllerTests {
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
- "META + P -> Launch Default Music",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
- ),
- TestData(
- "META + S -> Launch Default SMS",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_S),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
- ),
- TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -915,14 +912,14 @@ class KeyGestureControllerTests {
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + SHIFT + C -> Launch Default Contacts",
+ "META + SHIFT + P -> Launch Default Contacts",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_C
+ KeyEvent.KEYCODE_P
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -1424,6 +1421,45 @@ class KeyGestureControllerTests {
testKeyGestureInternal(test)
}
+ @Test
+ @Parameters(method = "customInputGesturesTestArguments")
+ fun testCustomKeyGesturesSavedAndLoadedByController(test: TestData) {
+ val userId = 10
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState
+ )
+ )
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ keyGestureController.addCustomInputGesture(userId, inputGestureData.aidlData)
+ testLooper.dispatchAll()
+
+ // Reinitialize the gesture controller simulating a login/logout for the user.
+ setupKeyGestureController()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
+ assertEquals(
+ "Test: $test doesn't produce correct number of saved input gestures",
+ 1,
+ savedInputGestures.size
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct input gesture data", inputGestureData,
+ InputGestureData(savedInputGestures[0])
+ )
+ }
+
class TouchpadTestData(
val name: String,
val touchpadGestureType: Int,
@@ -1502,6 +1538,39 @@ class KeyGestureControllerTests {
keyGestureController.unregisterKeyGestureHandler(handler, 0)
}
+ @Test
+ @Parameters(method = "customTouchpadGesturesTestArguments")
+ fun testCustomTouchpadGesturesSavedAndLoadedByController(test: TouchpadTestData) {
+ val userId = 10
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ keyGestureController.addCustomInputGesture(userId, inputGestureData.aidlData)
+ testLooper.dispatchAll()
+
+ // Reinitialize the gesture controller simulating a login/logout for the user.
+ setupKeyGestureController()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
+ assertEquals(
+ "Test: $test doesn't produce correct number of saved input gestures",
+ 1,
+ savedInputGestures.size
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct input gesture data", inputGestureData,
+ InputGestureData(savedInputGestures[0])
+ )
+ }
+
private fun testKeyGestureInternal(test: TestData) {
val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
@@ -1627,4 +1696,4 @@ class KeyGestureControllerTests {
return true
}
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
new file mode 100644
index 000000000000..281837920548
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.test.input
+
+import android.view.KeyCharacterMap
+import android.view.KeyEvent
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Tests for {@link KeyCharacterMap}.
+ *
+ * <p>Build/Install/Run:
+ * atest KeyCharacterMapTest
+ *
+ */
+class KeyCharacterMapTest {
+ @Test
+ fun testGetFallback() {
+ // Based off of VIRTUAL kcm fallbacks.
+ val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
+
+ // One modifier fallback.
+ assertEquals(
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON).keyCode,
+ KeyEvent.KEYCODE_LANGUAGE_SWITCH)
+
+ // Multiple modifier fallback.
+ assertEquals(
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON).keyCode,
+ KeyEvent.KEYCODE_BACK)
+
+ // No default button, fallback only.
+ assertEquals(
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0).keyCode,
+ KeyEvent.KEYCODE_DPAD_CENTER)
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml
new file mode 100644
index 000000000000..67d4397afe7d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <certificateTransparency enabled="true" />
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <domain>subdomain_user.android.com</domain>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain_user_ct.android.com</domain>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <domain>subdomain_inline.android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain_inline_ct.android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml b/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml
new file mode 100644
index 000000000000..c35fd71c3178
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain.android.com</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
index 0494f174f191..c6fe06858e3f 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -111,7 +111,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
private NetworkSecurityConfig getSystemStoreConfig() {
return new NetworkSecurityConfig.Builder()
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
}
@@ -141,7 +142,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -159,7 +161,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -178,7 +181,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), true, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -245,7 +249,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 81e05c1d4e42..542465d62a66 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -502,4 +502,47 @@ public class XmlConfigTests extends AndroidTestCase {
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
}
+
+ public void testCertificateTransparencyDomainConfig() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.ct_domains,
+ TestUtils.makeApplicationInfo());
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_user.android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_user_ct.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_inline.android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_inline_ct.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+ }
+
+ public void testCertificateTransparencyUserConfig() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.ct_users,
+ TestUtils.makeApplicationInfo());
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+ }
}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index af87bf724a30..49616c30b784 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -83,6 +83,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -112,6 +113,7 @@ public class CrashRecoveryTest {
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
+ private Executor mTestExecutor;
private Context mSpyContext;
// Keep track of all created watchdogs to apply device config changes
private List<PackageWatchdog> mAllocatedWatchdogs;
@@ -141,6 +143,7 @@ public class CrashRecoveryTest {
Manifest.permission.WRITE_DEVICE_CONFIG,
Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
mTestLooper = new TestLooper();
+ mTestExecutor = mTestLooper.getNewExecutor();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
@@ -231,31 +234,37 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
}
@@ -272,6 +281,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -281,6 +291,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -289,6 +300,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
}
@@ -305,18 +317,21 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -326,24 +341,28 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -352,6 +371,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -362,6 +382,7 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@@ -379,12 +400,14 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -394,6 +417,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -402,6 +426,7 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -412,6 +437,7 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@@ -739,14 +765,14 @@ public class CrashRecoveryTest {
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
- watchdog.registerHealthObserver(rollbackObserver);
+ watchdog.registerHealthObserver(rollbackObserver, mTestExecutor);
return rollbackObserver;
}
RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
setCrashRecoveryPropRescueBootCount(0);
RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
assertFalse(RescueParty.isRebootPropertySet());
- watchdog.registerHealthObserver(rescuePartyObserver);
+ watchdog.registerHealthObserver(rescuePartyObserver, mTestExecutor);
return rescuePartyObserver;
}
diff --git a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
index cd2ab86d6444..055e159ff0b6 100644
--- a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@@ -35,8 +37,6 @@ public class ExplicitHealthCheckServiceTest {
private ExplicitHealthCheckService mExplicitHealthCheckService;
private static final String PACKAGE_NAME = "com.test.package";
- private static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
- "android.service.watchdog.extra.health_check_passed_package";
@Before
public void setup() throws Exception {
@@ -52,7 +52,7 @@ public class ExplicitHealthCheckServiceTest {
IBinder binder = mExplicitHealthCheckService.onBind(new Intent());
CountDownLatch countDownLatch = new CountDownLatch(1);
RemoteCallback callback = new RemoteCallback(result -> {
- assertThat(result.get(EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
+ assertThat(result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
.isEqualTo(PACKAGE_NAME);
countDownLatch.countDown();
});
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 5a8a6bedf9eb..c64dc7296f0a 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -53,6 +53,7 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.util.AtomicFile;
import android.util.LongArrayQueue;
+import android.util.Slog;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -88,6 +89,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -119,6 +121,7 @@ public class PackageWatchdogTest {
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
+ private Executor mTestExecutor;
private Context mSpyContext;
// Keep track of all created watchdogs to apply device config changes
private List<PackageWatchdog> mAllocatedWatchdogs;
@@ -155,6 +158,7 @@ public class PackageWatchdogTest {
Manifest.permission.WRITE_DEVICE_CONFIG,
Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
mTestLooper = new TestLooper();
+ mTestExecutor = mTestLooper.getNewExecutor();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
@@ -226,7 +230,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -242,8 +247,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -260,7 +267,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -276,8 +284,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -294,7 +304,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -310,8 +321,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), LONG_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -330,13 +343,14 @@ public class PackageWatchdogTest {
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
// Start observing APP_A
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then advance time half-way
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
// Start observing APP_A again
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then advance time such that it should have expired were it not for the second observation
moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
@@ -358,15 +372,17 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog1.registerHealthObserver(observer1, mTestExecutor);
+ watchdog1.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog1.registerHealthObserver(observer2, mTestExecutor);
+ watchdog1.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
// Then advance time and run IO Handler so file is saved
mTestLooper.dispatchAll();
// Then start a new watchdog
PackageWatchdog watchdog2 = createWatchdog();
// Then resume observer1 and observer2
- watchdog2.registerHealthObserver(observer1);
- watchdog2.registerHealthObserver(observer2);
+ watchdog2.registerHealthObserver(observer1, mTestExecutor);
+ watchdog2.registerHealthObserver(observer2, mTestExecutor);
raiseFatalFailureAndDispatch(watchdog2,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -374,6 +390,7 @@ public class PackageWatchdogTest {
// We should receive failed packages as expected to ensure observers are persisted and
// resumed correctly
+ mTestLooper.dispatchAll();
assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
}
@@ -387,8 +404,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A below the threshold
for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) {
@@ -414,9 +433,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
-
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_B), SHORT_DURATION);
// Then fail APP_C (not observed) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -448,7 +468,8 @@ public class PackageWatchdogTest {
}
};
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A (different version) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -477,13 +498,17 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ watchdog.registerHealthObserver(observerNone, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
SHORT_DURATION);
- watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
+ watchdog.registerHealthObserver(observerHigh, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
SHORT_DURATION);
- watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
+ watchdog.registerHealthObserver(observerMid, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
SHORT_DURATION);
- watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
+ watchdog.registerHealthObserver(observerLow, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
SHORT_DURATION);
// Then fail all apps above the threshold
@@ -523,13 +548,17 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ watchdog.registerHealthObserver(observerNone, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
SHORT_DURATION);
- watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
+ watchdog.registerHealthObserver(observerHigh, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
SHORT_DURATION);
- watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
+ watchdog.registerHealthObserver(observerMid, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
SHORT_DURATION);
- watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
+ watchdog.registerHealthObserver(observerLow, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
SHORT_DURATION);
// Then fail all apps above the threshold
@@ -577,8 +606,10 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerFirst, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerSecond, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -641,8 +672,10 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerFirst, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerSecond, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -709,8 +742,10 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing for observer1 and observer2 with failure handling
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -731,8 +766,10 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
// Start observing for observer1 and observer2 with failure handling
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -762,8 +799,10 @@ public class PackageWatchdogTest {
// Start observing with explicit health checks for APP_A and APP_B respectively
// with observer1 and observer2
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B));
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -779,7 +818,8 @@ public class PackageWatchdogTest {
// Observer3 didn't exist when we got the explicit health check above, so
// it starts out with a non-passing explicit health check and has to wait for a pass
// otherwise it would be notified of APP_A failure on expiry
- watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer3, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer3, Arrays.asList(APP_A), SHORT_DURATION);
// Then expire observers
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -809,8 +849,9 @@ public class PackageWatchdogTest {
// Start observing with explicit health checks for APP_A and APP_B
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_B), LONG_DURATION);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -846,7 +887,7 @@ public class PackageWatchdogTest {
// Then set new supported packages
controller.setSupportedPackages(Arrays.asList(APP_C));
// Start observing APP_A and APP_C; only APP_C has support for explicit health checks
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
// Run handler so requests/cancellations are dispatched to the controller
mTestLooper.dispatchAll();
@@ -877,7 +918,8 @@ public class PackageWatchdogTest {
// package observation duration == LONG_DURATION
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), LONG_DURATION);
// Then APP_A has exceeded health check duration
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -908,7 +950,8 @@ public class PackageWatchdogTest {
// package observation duration == SHORT_DURATION / 2
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
// Forward time to expire the observation duration
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
@@ -981,7 +1024,7 @@ public class PackageWatchdogTest {
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1001,7 +1044,7 @@ public class PackageWatchdogTest {
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1022,7 +1065,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Fail APP_A below the threshold which should not trigger package failures
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1050,7 +1094,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
@@ -1075,15 +1120,16 @@ public class PackageWatchdogTest {
}
/**
- * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
- * an invalid durationMs.
+ * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
+ * offered an invalid durationMs.
*/
@Test
public void testInvalidMonitoringDuration_beforeExpiry() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
// Note: Don't move too close to the expiration time otherwise the handler will be thrashed
// by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
// small timeouts.
@@ -1097,15 +1143,16 @@ public class PackageWatchdogTest {
}
/**
- * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
- * an invalid durationMs.
+ * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
+ * offered an invalid durationMs.
*/
@Test
public void testInvalidMonitoringDuration_afterExpiry() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1127,7 +1174,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
// Raise 2 failures at t=0 and t=900 respectively
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1154,8 +1202,10 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
@@ -1174,7 +1224,8 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
@@ -1194,7 +1245,8 @@ public class PackageWatchdogTest {
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(true);
- watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
+ watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1212,7 +1264,8 @@ public class PackageWatchdogTest {
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(false);
- watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
+ watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1223,13 +1276,15 @@ public class PackageWatchdogTest {
/** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
@Test
public void testBootLoopDetection_meetsThreshold() {
+ Slog.w("hrm1243", "I should definitely be here try 1 ");
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isTrue();
}
@@ -1237,10 +1292,11 @@ public class PackageWatchdogTest {
public void testBootLoopDetection_meetsThresholdRecoverability() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isTrue();
}
@@ -1252,10 +1308,11 @@ public class PackageWatchdogTest {
public void testBootLoopDetection_doesNotMeetThreshold() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isFalse();
}
@@ -1268,10 +1325,11 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isFalse();
}
@@ -1286,11 +1344,12 @@ public class PackageWatchdogTest {
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1);
- watchdog.registerHealthObserver(bootObserver2);
+ watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
+ watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
@@ -1302,11 +1361,12 @@ public class PackageWatchdogTest {
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1);
- watchdog.registerHealthObserver(bootObserver2);
+ watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
+ watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
@@ -1319,7 +1379,7 @@ public class PackageWatchdogTest {
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) {
watchdog.noteBoot();
@@ -1333,7 +1393,7 @@ public class PackageWatchdogTest {
watchdog.noteBoot();
}
}
-
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
}
@@ -1342,7 +1402,7 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) {
watchdog.noteBoot();
}
@@ -1358,7 +1418,7 @@ public class PackageWatchdogTest {
for (int i = 0; i < 4; i++) {
watchdog.noteBoot();
}
-
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
}
@@ -1370,7 +1430,8 @@ public class PackageWatchdogTest {
public void testNullFailedPackagesList() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, List.of(APP_A), LONG_DURATION);
raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
assertThat(observer1.mMitigatedPackages).isEmpty();
@@ -1388,18 +1449,18 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog(testController, true);
TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(testObserver1);
- watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver1, List.of(APP_A), LONG_DURATION);
mTestLooper.dispatchAll();
TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(testObserver2);
- watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver2, List.of(APP_B), LONG_DURATION);
mTestLooper.dispatchAll();
TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3);
- watchdog.registerHealthObserver(testObserver3);
- watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver3, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver3, List.of(APP_C), LONG_DURATION);
mTestLooper.dispatchAll();
watchdog.unregisterHealthObserver(testObserver1);
@@ -1431,14 +1492,15 @@ public class PackageWatchdogTest {
public void testFailureHistoryIsPreserved() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A), SHORT_DURATION);
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
mTestLooper.dispatchAll();
assertThat(observer.mMitigatedPackages).isEmpty();
- watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A), LONG_DURATION);
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1453,7 +1515,8 @@ public class PackageWatchdogTest {
public void testMitigationSlidingWindow() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, List.of(APP_A),
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A),
PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2);
@@ -1895,6 +1958,7 @@ public class PackageWatchdogTest {
}
public boolean onExecuteBootLoopMitigation(int level) {
+ Slog.w("hrm1243", "I'm here " + level);
mMitigatedBootLoop = true;
mBootMitigationCounts.add(level);
return true;
diff --git a/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
index ad032fb2fba6..15a580c9e8f7 100644
--- a/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
+++ b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
@@ -186,6 +187,22 @@ public class BroadcastStickyCacheTest {
}
@Test
+ public void getIntent_queryActionTwiceWithNullResult_verifyRegisterReceiverIsCalledOnce()
+ throws RemoteException {
+ setActivityManagerMock(null);
+ final Intent intent = queryIntent(new IntentFilter(Intent.ACTION_DEVICE_STORAGE_FULL));
+ final Intent cachedIntent = queryIntent(
+ new IntentFilter(Intent.ACTION_DEVICE_STORAGE_FULL));
+
+ assertNull(intent);
+ assertNull(cachedIntent);
+
+ verify(mActivityManagerMock, times(1)).registerReceiverWithFeature(
+ eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(),
+ any(), anyString(), anyInt(), anyInt());
+ }
+
+ @Test
public void getIntent_querySameActionWithDifferentFilter_verifyRegisterReceiverCalledTwice()
throws RemoteException {
setActivityManagerMock(Intent.ACTION_DEVICE_STORAGE_LOW);
@@ -226,6 +243,6 @@ public class BroadcastStickyCacheTest {
when(ActivityManager.getService()).thenReturn(mActivityManagerMock);
when(mActivityManagerMock.registerReceiverWithFeature(any(), anyString(),
anyString(), anyString(), any(), any(), anyString(), anyInt(),
- anyInt())).thenReturn(new Intent(action));
+ anyInt())).thenReturn(action != null ? new Intent(action) : null);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 76be232c2fe3..74db6a5211a0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -659,7 +659,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
private void verifySetSafeModeAlarm(
boolean safeModeEnabledByCaller,
- boolean safeModeConfigFlagEnabled,
boolean expectingSafeModeEnabled)
throws Exception {
final VcnGatewayConnectionConfig config =
@@ -670,7 +669,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mock(VcnGatewayConnection.Dependencies.class);
setUpWakeupMessage(
mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, deps);
- doReturn(safeModeConfigFlagEnabled).when(mFeatureFlags).safeModeConfig();
final VcnGatewayConnection connection =
new VcnGatewayConnection(
@@ -694,37 +692,19 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
}
@Test
- public void testSafeModeEnabled_configFlagEnabled() throws Exception {
+ public void testSafeModeEnabled() throws Exception {
verifySetSafeModeAlarm(
true /* safeModeEnabledByCaller */,
- true /* safeModeConfigFlagEnabled */,
true /* expectingSafeModeEnabled */);
}
@Test
- public void testSafeModeEnabled_configFlagDisabled() throws Exception {
- verifySetSafeModeAlarm(
- true /* safeModeEnabledByCaller */,
- false /* safeModeConfigFlagEnabled */,
- true /* expectingSafeModeEnabled */);
- }
-
- @Test
- public void testSafeModeDisabled_configFlagEnabled() throws Exception {
+ public void testSafeModeDisabled() throws Exception {
verifySetSafeModeAlarm(
false /* safeModeEnabledByCaller */,
- true /* safeModeConfigFlagEnabled */,
false /* expectingSafeModeEnabled */);
}
- @Test
- public void testSafeModeDisabled_configFlagDisabled() throws Exception {
- verifySetSafeModeAlarm(
- false /* safeModeEnabledByCaller */,
- false /* safeModeConfigFlagEnabled */,
- true /* expectingSafeModeEnabled */);
- }
-
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
triggerChildOpened();
mTestLooper.dispatchAll();
diff --git a/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java b/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
index 03610128d269..c438163948fc 100644
--- a/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
+++ b/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
@@ -30,6 +30,7 @@ import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
@@ -42,6 +43,11 @@ public class CachedPropertyProcessor extends AbstractProcessor {
new IpcDataCacheComposer();
@Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
public Set<String> getSupportedAnnotationTypes() {
return new HashSet<String>(
ImmutableSet.of(CachedPropertyDefaults.class.getCanonicalName()));
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
index c3595b7ac288..272d8bb1793d 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -119,7 +119,8 @@ class ProtoLogCallProcessorImpl(
}
logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
- call.name.toString(), call, context), groupMap.getValue(groupName))
+ call.name.toString(), call, context), groupMap.getValue(groupName),
+ context.lineNumber)
} else if (call.name.id == "init") {
// No processing
} else {
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
index 8cd927a7cd0e..216241ac5a47 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -20,5 +20,11 @@ import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.expr.MethodCallExpr
interface ProtoLogCallVisitor {
- fun processCall(call: MethodCallExpr, messageString: String, level: LogLevel, group: LogGroup)
+ fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup,
+ lineNumber: Int
+ )
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 9222ff4bf52c..d8a2954545bb 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -69,7 +69,8 @@ object ProtoLogTool {
val messageString: String,
val logLevel: LogLevel,
val logGroup: LogGroup,
- val position: String
+ val position: String,
+ val lineNumber: Int,
)
private fun showHelpAndExit() {
@@ -435,9 +436,10 @@ object ProtoLogTool {
call: MethodCallExpr,
messageString: String,
level: LogLevel,
- group: LogGroup
+ group: LogGroup,
+ lineNumber: Int,
) {
- val logCall = LogCall(messageString, level, group, packagePath)
+ val logCall = LogCall(messageString, level, group, packagePath, lineNumber)
calls.add(logCall)
}
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index c478f5844de6..76df0674df65 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -91,7 +91,8 @@ class SourceTransformer(
call: MethodCallExpr,
messageString: String,
level: LogLevel,
- group: LogGroup
+ group: LogGroup,
+ lineNumber: Int,
) {
validateCall(call)
val processedCallStatement =
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
index de85411e4ffc..5af2d9440533 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -59,7 +59,7 @@ class ViewerConfigProtoBuilder : ProtoLogTool.ProtologViewerConfigBuilder {
.setLevel(
ProtoLogLevel.forNumber(log.logLevel.id))
.setGroupId(groupId)
- .setLocation(log.position)
+ .setLocation("${log.position}:${log.lineNumber}")
)
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
index 5e50f71d75cc..004d97babbad 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
@@ -42,7 +42,8 @@ class ProtoLogCallProcessorImplTest {
call: MethodCallExpr,
messageString: String,
level: LogLevel,
- group: LogGroup
+ group: LogGroup,
+ lineNumber: Int,
) {
calls.add(LogCall(call, messageString, level, group))
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 6cde7a72db53..674a907d68d9 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -158,7 +158,7 @@ class SourceTransformerTest {
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
invocation.arguments[0] as CompilationUnit
}
@@ -195,11 +195,11 @@ class SourceTransformerTest {
val calls = code.findAll(MethodCallExpr::class.java)
visitor.processCall(calls[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456)
visitor.processCall(calls[1], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 789)
visitor.processCall(calls[2], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
invocation.arguments[0] as CompilationUnit
}
@@ -236,7 +236,7 @@ class SourceTransformerTest {
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
"test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- true, true, "WM_TEST"))
+ true, true, "WM_TEST"), 123)
invocation.arguments[0] as CompilationUnit
}
@@ -273,7 +273,7 @@ class SourceTransformerTest {
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456)
invocation.arguments[0] as CompilationUnit
}
@@ -307,7 +307,7 @@ class SourceTransformerTest {
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"))
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"), 789)
invocation.arguments[0] as CompilationUnit
}
@@ -344,7 +344,7 @@ class SourceTransformerTest {
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
"test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- true, false, "WM_TEST"))
+ true, false, "WM_TEST"), 123)
invocation.arguments[0] as CompilationUnit
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
index 1a20d4c5bad6..bcbc8790e170 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
@@ -50,9 +50,9 @@ class ViewerConfigJsonBuilderTest {
fun processClass() {
val logCallRegistry = ProtoLogTool.LogCallRegistry()
logCallRegistry.addLogCalls(listOf(
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH),
- LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)))
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+ LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH, 456),
+ LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH, 789)))
val parsedConfig = parseConfig(
configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
@@ -69,9 +69,9 @@ class ViewerConfigJsonBuilderTest {
fun processClass_nonUnique() {
val logCallRegistry = ProtoLogTool.LogCallRegistry()
logCallRegistry.addLogCalls(listOf(
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)))
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 456),
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 789)))
val parsedConfig = parseConfig(
configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
index 74a8de7f70c0..dfc66405eec9 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
@@ -55,8 +55,8 @@ class ViewerConfigProtoBuilderTest {
val logCallRegistry = ProtoLogTool.LogCallRegistry()
logCallRegistry.addLogCalls(listOf(
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH),
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+ LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH, 456),
))
val rawProto = configBuilder.build(GROUPS, logCallRegistry.getStatements())
@@ -65,4 +65,22 @@ class ViewerConfigProtoBuilderTest {
Truth.assertThat(viewerConfig.groupsCount).isEqualTo(GROUPS.size)
Truth.assertThat(viewerConfig.messagesCount).isLessThan(GROUPS.size)
}
+
+ @Test
+ fun includesLineNumberInLocation() {
+ val configBuilder = ViewerConfigProtoBuilder()
+
+ val logCallRegistry = ProtoLogTool.LogCallRegistry()
+ logCallRegistry.addLogCalls(listOf(
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+ LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH, 456),
+ ))
+
+ val rawProto = configBuilder.build(GROUPS, logCallRegistry.getStatements())
+
+ val viewerConfig = ProtoLogViewerConfig.parseFrom(rawProto)
+
+ Truth.assertThat(viewerConfig.messagesList[0].location).isEqualTo("$PATH:123")
+ Truth.assertThat(viewerConfig.messagesList[1].location).isEqualTo("$PATH:456")
+ }
} \ No newline at end of file
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
index 100d869a663f..c51c6d661314 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
@@ -17,8 +17,12 @@
package com.android.systemfeatures
import android.annotation.SdkConstant
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeSpec
import java.io.IOException
import javax.annotation.processing.AbstractProcessor
@@ -27,6 +31,7 @@ import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
import javax.tools.Diagnostic
/*
@@ -35,7 +40,16 @@ import javax.tools.Diagnostic
* <p>The output is a single class file, `com.android.internal.pm.SystemFeaturesMetadata`, with
* properties computed from feature constant definitions in the PackageManager class. This
* class is only produced if the processed environment includes PackageManager; all other
- * invocations are ignored.
+ * invocations are ignored. The generated API is as follows:
+ *
+ * <pre>
+ * package android.content.pm;
+ * public final class SystemFeaturesMetadata {
+ * public static final int SDK_FEATURE_COUNT;
+ * // @return [0, SDK_FEATURE_COUNT) if an SDK-defined system feature, -1 otherwise.
+ * public static int maybeGetSdkFeatureIndex(String featureName);
+ * }
+ * </pre>
*/
class SystemFeaturesMetadataProcessor : AbstractProcessor() {
@@ -56,19 +70,31 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
return false
}
- // We're only interested in feature constants defined in PackageManager.
- var featureCount = 0
- roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach {
- if (
- it.enclosingElement == packageManagerType &&
- it.getAnnotation(SdkConstant::class.java).value ==
- SdkConstant.SdkConstantType.FEATURE
- ) {
- featureCount++
- }
- }
+ // Collect all FEATURE-annotated fields from PackageManager, and
+ // 1) Use the field values to de-duplicate, as there can be multiple FEATURE_* fields that
+ // map to the same feature string name value.
+ // 2) Ensure they're sorted to ensure consistency and determinism between builds.
+ val featureVarNames =
+ roundEnv
+ .getElementsAnnotatedWith(SdkConstant::class.java)
+ .asSequence()
+ .filter {
+ it.enclosingElement == packageManagerType &&
+ it.getAnnotation(SdkConstant::class.java).value ==
+ SdkConstant.SdkConstantType.FEATURE
+ }
+ .mapNotNull { element ->
+ (element as? VariableElement)?.let { varElement ->
+ varElement.getConstantValue()?.toString() to
+ varElement.simpleName.toString()
+ }
+ }
+ .toMap()
+ .values
+ .sorted()
+ .toList()
- if (featureCount == 0) {
+ if (featureVarNames.isEmpty()) {
// This is fine, and happens for any environment that doesn't include PackageManager.
return false
}
@@ -77,16 +103,8 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
TypeSpec.classBuilder("SystemFeaturesMetadata")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addJavadoc("@hide")
- .addField(
- FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
- .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
- .addJavadoc(
- "The number of `@SdkConstant` features defined in PackageManager."
- )
- .addJavadoc("@hide")
- .initializer("\$L", featureCount)
- .build()
- )
+ .addFeatureCount(featureVarNames)
+ .addFeatureIndexLookup(featureVarNames)
.build()
try {
@@ -104,7 +122,71 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
return true
}
+ private fun TypeSpec.Builder.addFeatureCount(
+ featureVarNames: Collection<String>
+ ): TypeSpec.Builder {
+ return addField(
+ FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
+ .addJavadoc(
+ "# of {@link android.annotation.SdkConstant}` features in PackageManager."
+ )
+ .addJavadoc("\n\n@hide")
+ .initializer("\$L", featureVarNames.size)
+ .build()
+ )
+ }
+
+ private fun TypeSpec.Builder.addFeatureIndexLookup(
+ featureVarNames: Collection<String>
+ ): TypeSpec.Builder {
+ // NOTE: This was initially implemented in terms of a single, long switch() statement.
+ // However, this resulted in:
+ // 1) relatively large compiled code size for the lookup method (~20KB)
+ // 2) worse runtime lookup performance than a simple ArraySet
+ // The ArraySet approach adds just ~1KB to the code/image and is 2x faster at runtime.
+
+ // Provide the initial capacity of the ArraySet for efficiency.
+ addField(
+ FieldSpec.builder(
+ ParameterizedTypeName.get(ARRAYSET_CLASS, ClassName.get(String::class.java)),
+ "sFeatures",
+ )
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
+ .initializer("new ArraySet<>(\$L)", featureVarNames.size)
+ .build()
+ )
+
+ // Use a temp array + Collections.addAll() to minimizes the generated code size.
+ addStaticBlock(
+ CodeBlock.builder()
+ .add("final \$T[] features = {\n", String::class.java)
+ .indent()
+ .apply { featureVarNames.forEach { add("\$T.\$N,\n", PACKAGEMANAGER_CLASS, it) } }
+ .unindent()
+ .addStatement("}")
+ .addStatement("\$T.addAll(sFeatures, features)", COLLECTIONS_CLASS)
+ .build()
+ )
+
+ // Use ArraySet.indexOf to provide the implicit feature index mapping.
+ return addMethod(
+ MethodSpec.methodBuilder("maybeGetSdkFeatureIndex")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addJavadoc("@return an index in [0, SDK_FEATURE_COUNT) for features defined ")
+ .addJavadoc("in PackageManager, else -1.")
+ .addJavadoc("\n\n@hide")
+ .returns(Int::class.java)
+ .addParameter(String::class.java, "featureName")
+ .addStatement("return sFeatures.indexOf(featureName)")
+ .build()
+ )
+ }
+
companion object {
private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName
+ private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
+ private val ARRAYSET_CLASS = ClassName.get("android.util", "ArraySet")
+ private val COLLECTIONS_CLASS = ClassName.get("java.util", "Collections")
}
}
diff --git a/tools/systemfeatures/tests/src/ArraySet.java b/tools/systemfeatures/tests/src/ArraySet.java
new file mode 100644
index 000000000000..0eb8f298bd89
--- /dev/null
+++ b/tools/systemfeatures/tests/src/ArraySet.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.util.ArrayList;
+
+/** Stub for testing, we extend ArrayList to get indexOf() for free. */
+public final class ArraySet<K> extends ArrayList<K> {
+ public ArraySet(int capacity) {
+ super(capacity);
+ }
+
+ @Override
+ public boolean add(K k) {
+ if (!contains(k)) {
+ return super.add(k);
+ }
+ return false;
+ }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
index 4ffb5b979d75..74ce6daaffc4 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -16,10 +16,16 @@
package com.android.systemfeatures;
+import static com.android.internal.pm.SystemFeaturesMetadata.maybeGetSdkFeatureIndex;
+
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.PackageManager;
+
import com.android.internal.pm.SystemFeaturesMetadata;
+import com.google.common.collect.Range;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -33,4 +39,17 @@ public class SystemFeaturesMetadataProcessorTest {
// It defines 5 annotated features, and any/all other constants should be ignored.
assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
}
+
+ @Test
+ public void testSdkFeatureIndex() {
+ // Only SDK-defined features return valid indices.
+ final Range validIndexRange = Range.closedOpen(0, SystemFeaturesMetadata.SDK_FEATURE_COUNT);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_PC)).isIn(validIndexRange);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_VULKAN)).isIn(validIndexRange);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_NOT_ANNOTATED)).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.NOT_FEATURE)).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("foo")).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("0")).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("")).isEqualTo(-1);
+ }
}